| | 1 | = DatabaseCreation = |
| | 2 | |
| | 3 | == Опис == |
| | 4 | |
| | 5 | Во оваа фаза е имплементирана релационата база на податоци за VQEMS системот во PostgreSQL. |
| | 6 | |
| | 7 | Структурата на базата е дефинирана со DDL скриптата, додека податоците потребни за работа и тестирање на системот се генерираат со посебна скрипта. |
| | 8 | |
| | 9 | Основните датотеки се: |
| | 10 | |
| | 11 | * [attachment:ddl.sql ddl.sql] - дефиниција на табелите, клучевите и ограничувањата |
| | 12 | * [attachment:seed_generator.py seed_generator.py] - генерирање на податоци за пополнување на базата |
| | 13 | |
| | 14 | == DML - работа со податоците == |
| | 15 | |
| | 16 | По креирањето на структурата на базата, потребно е табелите да се пополнат со податоци кои ќе овозможат реално тестирање на системот. |
| | 17 | |
| | 18 | Поради големиот број на потребни записи, податоците не се внесуваат рачно со поединечни `INSERT` наредби. Наместо тоа, се користи Python скрипта која автоматски генерира податоци за сите табели. |
| | 19 | |
| | 20 | Скриптата користи `Faker` за генерирање на реалистични вредности како: |
| | 21 | |
| | 22 | * имиња и презимиња |
| | 23 | * компании |
| | 24 | * email адреси |
| | 25 | * URL адреси |
| | 26 | * текстови за reviews и disputes |
| | 27 | * датуми |
| | 28 | * буџети |
| | 29 | * договори и проекти |
| | 30 | |
| | 31 | За да може истиот сет на податоци повторно да се генерира, се користи фиксен seed: |
| | 32 | |
| | 33 | {{{#!python |
| | 34 | SEED = 42 |
| | 35 | |
| | 36 | fake = Faker() |
| | 37 | Faker.seed(SEED) |
| | 38 | random.seed(SEED) |
| | 39 | }}} |
| | 40 | |
| | 41 | == Редослед на внесување на податоците == |
| | 42 | |
| | 43 | Податоците се генерираат според зависностите помеѓу табелите, со цел да се почитуваат странските клучеви дефинирани во базата. |
| | 44 | |
| | 45 | Редоследот е приближно: |
| | 46 | |
| | 47 | 1. lookup табели (`Role`, `Permission`, `Industry`, `Project_Status`, `Technology`, ...) |
| | 48 | 2. `Vendor` и `Client` |
| | 49 | 3. `User` и неговите специјализации |
| | 50 | 4. `Vendor_Subscription` |
| | 51 | 5. `Client_Vendor_Contract` |
| | 52 | 6. `Project` |
| | 53 | 7. `Project_Technology` |
| | 54 | 8. `Project_Budget_Audit` |
| | 55 | 9. `Project_Status_History` |
| | 56 | 10. `Review` |
| | 57 | 11. `Review_Score` |
| | 58 | 12. `Dispute_Ticket` |
| | 59 | |
| | 60 | Со ова се обезбедува записите кои се референцираат преку foreign key да постојат пред да бидат употребени во зависните табели. |
| | 61 | |
| | 62 | == Пример - генерирање на Project записи == |
| | 63 | |
| | 64 | За табелата `Project` се генерираат записи со договор, статус, име, почетен и краен датум и буџет: |
| | 65 | |
| | 66 | {{{#!python |
| | 67 | NUM_PROJECTS = 1_400_000 |
| | 68 | |
| | 69 | with csv_writer( |
| | 70 | "project.csv", |
| | 71 | [ |
| | 72 | "project_id", |
| | 73 | "contract_id", |
| | 74 | "status_id", |
| | 75 | "project_name", |
| | 76 | "start_date", |
| | 77 | "end_date", |
| | 78 | "budget", |
| | 79 | ], |
| | 80 | ) as w: |
| | 81 | for i in range(NUM_PROJECTS): |
| | 82 | sd = rand_date(0, 4) |
| | 83 | |
| | 84 | ``` |
| | 85 | ed = ( |
| | 86 | sd + timedelta(days=random.randint(30, 730)) |
| | 87 | if random.random() > 0.2 |
| | 88 | else None |
| | 89 | ) |
| | 90 | |
| | 91 | w.writerow( |
| | 92 | { |
| | 93 | "project_id": i + 1, |
| | 94 | "contract_id": random.choice(contract_ids), |
| | 95 | "status_id": random.choice(status_ids), |
| | 96 | "project_name": fake.catch_phrase(), |
| | 97 | "start_date": sd, |
| | 98 | "end_date": ed, |
| | 99 | "budget": round(random.uniform(1_000, 250_000), 2), |
| | 100 | } |
| | 101 | ) |
| | 102 | ``` |
| | 103 | |
| | 104 | }}} |
| | 105 | |
| | 106 | При генерирањето, `contract_id` и `status_id` се избираат од веќе постоечките идентификатори, со што се задржува референцијалниот интегритет на податоците. |
| | 107 | |
| | 108 | == Пример - Reviews и Review Scores == |
| | 109 | |
| | 110 | За дел од проектите се генерира review: |
| | 111 | |
| | 112 | {{{#!python |
| | 113 | reviewed_pids = random.sample( |
| | 114 | range(1, NUM_PROJECTS + 1), |
| | 115 | k=int(NUM_PROJECTS * 0.70) |
| | 116 | ) |
| | 117 | }}} |
| | 118 | |
| | 119 | За секој review потоа се генерира оценка за секоја дефинирана димензија: |
| | 120 | |
| | 121 | {{{#!python |
| | 122 | for rid in review_ids: |
| | 123 | for did in dim_ids: |
| | 124 | w.writerow( |
| | 125 | { |
| | 126 | "review_id": rid, |
| | 127 | "dimension_id": did, |
| | 128 | "score_value": random.randint(1, 5), |
| | 129 | } |
| | 130 | ) |
| | 131 | }}} |
| | 132 | |
| | 133 | На овој начин еден review има повеќе оценки, согласно моделот `Review` - `Review_Score` - `Rating_Dimension`. |
| | 134 | |
| | 135 | == Пример - врски помеѓу проект и технологии == |
| | 136 | |
| | 137 | Еден проект може да користи повеќе технологии. За секој проект случајно се избираат од една до пет технологии: |
| | 138 | |
| | 139 | {{{#!python |
| | 140 | for pid in project_ids: |
| | 141 | for tid in random.sample(tech_ids, random.randint(1, 5)): |
| | 142 | w.writerow( |
| | 143 | { |
| | 144 | "project_id": pid, |
| | 145 | "technology_id": tid |
| | 146 | } |
| | 147 | ) |
| | 148 | }}} |
| | 149 | |
| | 150 | Со ова се пополнува many-to-many релацијата `Project_Technology`. |
| | 151 | |
| | 152 | == Генерирање на податоците == |
| | 153 | |
| | 154 | Скриптата може да се стартува со: |
| | 155 | |
| | 156 | {{{#!bash |
| | 157 | python3 seed_generator.py seed_data |
| | 158 | }}} |
| | 159 | |
| | 160 | Како резултат се генерираат CSV датотеки за табелите во базата, кои потоа се користат за нејзино пополнување. |
| | 161 | |
| | 162 | Поголемите табели содржат голем број записи за да се симулира реална употреба на апликацијата. На пример: |
| | 163 | |
| | 164 | || '''Табела''' || '''Број на записи''' || |
| | 165 | || Vendor || 5,000 || |
| | 166 | || Client || 20,000 || |
| | 167 | || User || 77,000 || |
| | 168 | || Client_Vendor_Contract || 250,000 || |
| | 169 | || Project || 1,400,000 || |
| | 170 | || Review || 980,000 || |
| | 171 | || Review_Score || 4,900,000 || |
| | 172 | |
| | 173 | Покрај нив, `Project_Technology` и `Project_Status_History` генерираат повеќе записи за секој проект, со што и тие достигнуваат многу голем број на редици. |
| | 174 | |
| | 175 | == Заклучок == |
| | 176 | |
| | 177 | Со DDL скриптата е дефинирана структурата на PostgreSQL базата, додека DML делот е поддржан преку автоматско генерирање на реалистични податоци за нејзино пополнување. |
| | 178 | |
| | 179 | Податоците ги следат врските и ограничувањата дефинирани во релациониот модел и обезбедуваат доволно голем dataset за понатамошно тестирање, работа со погледи и оптимизација на упити. |