Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Difference From 00532db4911111ae To 5c2d6c24ac6f1a49
2022-07-01
| ||
15:31 | Tweaked the formatting of avg update times (show window size, elements going into the result) Bugfix: Normalize the project name before putting it into lists. Flatten to single line, remove superfluous whitespace. Look at the description of https://github.com/luaforge/tclua. The multi-line broke MD formatting of the table) Added run time stamps to site generator (only at the major places) check-in: b6d8811f29 user: work tags: vcs-plugged | |
14:21 | 1. db migration, main and site 1. mirror set renamed to project. 1. updated cmdr spec, most commands 1. several commands switch to take repos instead of projects 1. completed the conversion of common vcs to call out to the backoffice code 1. reworked repo, store, vcs handling for the new schema 1. iterated over commands to check for issues and fix them 1. previous not complete, see merge, split, web pages 1. even so, the majority of the functionality looks to be working again 1. especially add/update of repos, i.e. the core. 1. and yet, still more testing needed to validate handling of issues with network and/or repo servers. 1. issues should be more localised however, as access (especially to github) should now use smaller requests overall 1. website, touch up of generated pages (lists, store details, new list of forks for > 5 forks of a repo) 1. see TODO.md for general things outside of fixes check-in: 5c2d6c24ac user: work tags: vcs-plugged | |
2022-06-28
| ||
15:19 | Fix v2/v3 repository migration check-in: 0f1fc429ce user: work tags: vcs-plugged | |
14:06 | Fix eols in scratch area check-in: e0637d5770 user: work tags: vcs-plugged | |
14:04 | Blind work in vcs layer have more commands call on the backoffice code. Untested. Likely broken. check-in: 00532db491 user: work tags: vcs-plugged | |
2019-10-30
| ||
17:30 | `m vcs *`: - Converted `url-to-name` (was `name-from-url`) - Converted `update`. - Removed `revs`, `remotes`. - Still kept: `detect`. - Added tests for the converted commands. check-in: ce42f93675 user: aku tags: vcs-plugged | |
Added TODO.md.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | --- 1. Check and fix merge 1. Check and fix split 1. Website - want more pages - statistics, project index/details 1. Test add/update handling of network/server issues, i.e. disconnects, timeouts, ... --- 1. merge - logic still based on projects -- rework to place repos at center 1. projects - list projects 1. projects - show project details 1. need stats command - summary of projects, repos, stores 1. repos - need commands to move repos between projects 1. repos - need commands to remove projects note: currently auto-removing a project when last repo removed - extend this to moves ? 1. note! forks and their origin can all be in different projects 1. new list - show only primary repos, exclude forks 1. log information saved at stores - should be at repos - issues happen when acessing a repo alternate: keep with store (on disk), but separate logs per repo. 1. refresh stores ? - old github stores can be big, containing shared data from all forks ... kill and recreate store to reduce it ? better: - rename old github repo projects, then re-add => new stores - disable old stores - hide from web indices ? (another repo flag) - more disk space - but keeps old state until new setup has initialized - Save old database, and pull the `store_github_forks` data for assessment of repos and their forks => handle the small ones first 1. export command (`dump`?) to save a store with associated metadata to external directory => save old github stores before removing from management 1. more various internal commands between packages - support for various list commands should be in repo now, instead of store 1. mirror config store - changes - broken |
Changes to _scratch_/gh-org-pull.
1 2 3 4 5 6 7 8 9 10 | #!/usr/bin/env tclsh ## -*- tcl -*- # # ## ### ##### ######## ############# ###################### # Pull a list of github repositories for a person or org. # Resolve forked repositories to the main repository. # Print the result as a mirror import spec. # # ## ### ##### ######## ############# ###################### | > | 1 2 3 4 5 6 7 8 9 10 11 | #!/usr/bin/env tclsh ## -*- tcl -*- # # ## ### ##### ######## ############# ###################### ## Syntax: $0 user-or-org ?name? # Pull a list of github repositories for a person or org. # Resolve forked repositories to the main repository. # Print the result as a mirror import spec. # # ## ### ##### ######## ############# ###################### |
︙ | ︙ |
Changes to _scratch_/git-tag-check.
1 2 3 4 5 6 7 8 9 10 | #!/usr/bin/env tclsh ## -*- tcl -*- # # ## ### ##### ######## ############# ###################### package require Tcl 8.6 package require m::futil proc main {} { global argv | > | 1 2 3 4 5 6 7 8 9 10 11 | #!/usr/bin/env tclsh ## -*- tcl -*- # # ## ### ##### ######## ############# ###################### ## Syntax: $0 gitdir package require Tcl 8.6 package require m::futil proc main {} { global argv |
︙ | ︙ |
Changes to _scratch_/v3-exec.tcl.
︙ | ︙ |
Changes to _scratch_/vcs-api-3-schema.md.
︙ | ︙ | |||
27 28 29 30 31 32 33 | ### Failure Flag for attend. Note: Need a command to re-parent|elect-new-primary Query github to determine parent of a repo. | < | 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | ### Failure Flag for attend. Note: Need a command to re-parent|elect-new-primary Query github to determine parent of a repo. ## Magic remote ### Success Store is updated, nothing more. |
︙ | ︙ |
Added doc/schema-v2-issues.md.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | # V2 Schema Issues 1. Nit: `Mirror set` is not a good name for the concept. `Project` is better. 2. The `Mirror set`, `Repository`, and `Store` relations are not nicely modeled in the tables. Using the indirect coupling of the last through the `Mirror set`/`VCS` combination is messy. The relation that a `repository` __has a_ (backing) `store` (1:n) is the core, and not modeled. 3. The various fields of the `store_times` table belong to `store`, and `repository`. It is unclear and not remembered anymore why an adjunct table was added to the system, instead of the entity properly extended. 4. Nit: The `store_times.attend` field should be named `has_issues`. 5. Do we truly need current and previous values for size and commits ? 6. Table `store_github_forks` is IMHO superfluous with the ideas around changing the fork handling currently hidden in the github driver. See next point. 7. The github driver, with its complex internal handling of forks in a single store is messy and fragile. The fragility is mainly around the handling of tags per fork, and having to add/remove/rename/readd origins. The other issue is that various git/hub operations are linear in the number of forks. This becomes problematic above a 100 or so forks. It definitely prevents using some kind of timeout to break out of stuck processes. It cannot be decided if the process is truly stuck, or simply crawling through the pile of forks. |
Added doc/schema-v2.md.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 | # V2 - Mirroring. Backing up Sources As of 2019-10-03T21:20 (`lib/db/db.tcl`, `SETUP-201910032120` and all preceding). Streamlined v1 to basics. - Removed VCS operation hooks. VCS support is fixed, no plugin system. Near-plugin system through extensible set of VCS specific packages - Removed tags. Not needed for an initial setup. ## Entities ### Overview |Table |Description | |--- |--- | |mirror_set |Group of repositories, same logical project | |mset_pending |Projects waiting for update in current cycle | |rejected |Rejected repositories, for use by future submissions | |reply |Mail replies for submission handling | |repository |A set of versioned files to back up | |rolodex |Shorthand repository references, recently seen | |state |Global system state and config | |store |Local holder of a repository backup | |store_github_forks |Number of forks per github store | |store_times |Cycle information per store | |submission |Submitted repositories waiting for handling | |submission_handled |Handled submissions for next sync with site database | |version_control_system |Information about supported VCS | ### Examples |Entity |Example | |--- |--- | |Repository | | | |github@github:andreas.kupries/marpa | | |https://chiselapp.com/user/andreas-kupries/repository/marpa | | |https://core.tcl-lang.org/akupries/marpa | |Mirror Set | | | |Tcl Marpa | |Version Control System | | | |bazaar | | |cvs | | |fossil (__+__) | | |git, github (__+__) | | |mercurial (hg) (__+__) | | |monotone | | |rcs | | |sccs | | |svn (__+__) | ### Core Relations 1. A `repository` __is managed by a__ `version control system` (n:1) 1. A `repository` __belongs to a__ `mirror set` (n:1) 1. A `store` __is managed by a__ `version control system` (n:1) 1. A `store` __belongs to a__ `mirror set` (n:1) 1. A `repository` __has a_ (backing) `store` (1:n) The above as a diagram, with some of the adjunct tables added, and the last relation __not__ shown. ``` Mset Pending ----\ \-> /--> Mirror Set <--------------\ Rolodex --> Repository Store <-- Store Times \--> Version Control System <--/ \ <-- Store Github Forks ``` ### Entity Attributes |Entity |Field |Type |Modifiers |Comments | |--- |--- |--- |--- |--- | |schema | | | | | | |key |text |PK |fix: `version` | | |version |int | | | |~ |~ |~ |~ |~ | |mirror_set | | | | | | |id |int |PK | | | |name |text |unique | | |mset_pending | | | | | | |id |int |PK, FK mirror_set | | |rejected | | | | | | |id |int |PK | | | |url |text |unique | | | |reason |text | | | |reply | | | | | | |id |int |PK | | | |name |text |unique | | | |automail |bool | |Send mail by default | | |isdefault |bool | |Use when no reason given | | |text |text | | | |repository | | | | | | |id |int |PK | | | |url |text |unique |Loction | | |vcs |int |FK version_control_... | __index 1__ | | |mset |int |FK mirror_set | __index 1__ | | |active |bool | | | |rolodex | | | | | | |id |int |PK | | | |repository |int |unique, FK repository | | |state | | | | | | |name |text |PK | | | |value |text | | | |store | | | | | | |id |int |PK | | | |vcs |int |FK version_control_... | __unique 1__ | | |mset |int |FK mirror_set | __unique 1__ | | |size_kb |int | |Kilobyte | | |size_previous |int | |ditto | | |commits_current |int | | | | |commits_previous |int | | | |store_github_forks | | | | | | |store |int |PK, FK store | | | |nforks |int | | | |store_times | | | | | | |store |int |PK, FK store | | | |created |int | |Epoch | | |updated |int | |Epoch | | |changed |int | |Epoch | | |attend |bool | |Has issues | | |min_seconds |int | | | | |max_seconds |int | | | | |window_seconds |text | |CSV, last N | |submission | | | | | | |id |int |PK | | | |session |text | __unique 1__ | | | |url |text | __unique 1__ | index 1 | | |vcode |text |nullable | VCS.code | | |description |text |nullable | | | |email |text | |subm. email | | |submitter |text |nullable |subm. name | | |sdate |int | |epoch, index 2 | |submission_handled | | | | | | |session |text | __unique 1__ | | | |url |text | __unique 1__ | | |version_control_system | | | | | | |id |int |PK | | | |code |text |unique |Short tag | | |name |text |unique |Human Readable | ## State keys and semantics |Key |Meaning | |--- |--- | |limit |State, #repos per `list` page | |start-of-current-cycle |State, epoch when update cycle started | |start-of-previous-cycle |State, epoch, previous update cycle | |store |State, path to stores on disk | |store-window-size |State, #of update durations to retain | |take |State, #mirror sets to update per run | |top |State, repo shown at top of `list` | |~ |~ | |mail-debug |Mail transport, debug flag | |mail-host |Mail transport, smtpd host | |mail-pass |Mail transport, smtp password | |mail-port |Mail transport, smtpd port | |mail-sender |Mail transport, smtp sender | |mail-tls |Mail transport, tls flag | |mail-user |Mail transport, smtp user | |~ |~ | |mail-footer |Mail config, footer text | |mail-header |Mail config, header text | |mail-width |Mail config, table width limit | |report-mail-destination |Mail config, destination | |~ |~ | |site-active |Site config, flag of use | |site-logo |Site config, url to logo | |site-mgr-mail |Site config, email of manager | |site-mgr-name |Site config, name of manager | |site-store |Site config, path to site on disk | |site-title |Site config, general title | |site-url |Site config, url of site | |
Added doc/schema-v3.md.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 | # V3 - Mirroring. Backing up Sources In planning, to address the [issues of V2](schema-v2-issues.md) ## Changes 1. Renamed `mirror_set` to `project`. 1. Renamed `mset_pending` to `repo_pending`. 1. Moved `store_times.created` to `store`. 1. Moved `store_times.updated` to `store`. 1. Moved `store_times.changed` to `store`. 1. Moved `store_times.attend` to `repository.has_issues`. 1. Moved `store_times.min_seconds` to `repository.min_duration`. 1. Moved `store_times.max_seconds` to `repository.max_duration`. 1. Moved `store_times.window_seconds` to `repository`. 1. Dropped table `store_times`. 1. Dropped table `store_github_forks`. 1. Redid the core relations. 1. Made the fork handling explicit in the schema. 1. Removed `store.mset`. 1. Renamed `repository.mset` to `repository.project`. 1. Added `repository.store`. 1. Added `repository.checked`. Redesign of the fork handling: 1. A VCS driver may report forks on `setup` and `update` operations. 1. The reported forks are automatically added to the project of the primary (aka fork_origin), as their own repositories, and activated. Unreported known forks are deactivated. __Not__ removed. 1. Forks get their own store. While this blows up the disk space needed to handle the project it also makes handling much easier, as there is no need to fiddle with git(hub) tags and origins. If desired it is always possible to manually merge stores. Not recommended for git. ## Entities ### Overview |Table |Description | |--- |--- | |project |Group of repositories, same logical project | |rejected |Rejected repositories, for use by future submissions | |reply |Mail replies for submission handling | |repo_pending |Repositories waiting for update in current cycle | |repository |A set of versioned files to back up | |rolodex |Shorthand repository references, recently seen | |state |Global system state and config | |store |Local holder of a repository backup | |submission |Submitted repositories waiting for handling | |submission_handled |Handled submissions for next sync with site database | |version_control_system |Information about supported VCS | ### Examples |Entity |Example | |--- |--- | |Repository | | | |github@github:andreas.kupries/marpa | | |https://chiselapp.com/user/andreas-kupries/repository/marpa | | |https://core.tcl-lang.org/akupries/marpa | |Project | | | |Tcl Marpa | |Version Control System | | | |bazaar | | |cvs | | |fossil (__+__) | | |git, github (__+__) | | |mercurial (hg) (__+__) | | |monotone | | |rcs | | |sccs | | |svn (__+__) | ### Core Relations 1. A `project` __has__ zero or more `repositories` (1:n). 1. __(x)__ A `repository` __belongs to__ a single `project` (n:1). 1. __(x)__ A `repository` __is managed by__ a single `version control system` (n:1). 1. A `version control system` __manages__ zero or more `repositories` (1:n). 1. __(x)__ A `repository` __has_ a single (backing) `store` (1:n). 1. A `store` __contains the data__ of one or more __repositories (1:n). 1. __(x)__ A `store` __is managed by__ a single `version control system` (n:1). 1. A `version control system` __manages__ zero or more `stores` (1:n). 1. __(x)__ A `repository` may __have__ a parent `repository` it is forked from (n:1). 1. A `repository` __has__ zero or more forked `repositories` (1:n). A checking contraint: 1. A `repository` and its backing `store` are managed by the same `version control system`. IOW `repository.store.vcs == repository.vcs`. Below we see the above as diagram, with the relations marked __(x)__ as the shown arrows / foreign key references, and some adjunct tables added. ``` rolodex ------>\ repo_pending -->\ project <-- repository ------------------->\ \--> store ---------> version_control_system ``` ### Entity Attributes |Entity |Field |Type |Modifiers |Comments | |--- |--- |--- |--- |--- | |schema | | | | | | |key |text |PK |fix: `version` | | |version |int | | | |~ |~ |~ |~ |~ | |__Main Database__ | | | | | |~ |~ |~ |~ |~ | |project | | | | | | |id |int |PK | | | |name |text |unique | | |repo_pending | | | | | | |id |int |PK, FK repository | | |rejected | | | | | | |id |int |PK | | | |url |text |unique | | | |reason |text | | | |reply | | | | | | |id |int |PK | | | |name |text |unique | | | |automail |bool | |Send mail by default | | |isdefault |bool | |Use when no reason given | | |text |text | | | |repository | | | | | | |id |int |PK | | | |url |text |unique |Location | | |project |int |FK project | __index 1__ | | |vcs |int |FK version_control_... | __index 1__ | | |store |int |FK store | __index 2__ | | |fork_origin |int |FK repository, nullable| __index 3__ | | |is_active |bool | | | | |has_issues |bool | |Has issues | | |min_duration |int | | | | |max_duration |int | | | | |window_duration |text | |CSV, last N | | |checked |int | |epoch | |rolodex | | | | | | |id |int |PK | | | |repository |int |unique, FK repository | | |state | | | | | | |name |text |PK | | | |value |text | | | |store | | | | | | |id |int |PK | | | |vcs |int |FK version_control_... | | | |size_kb |int | |Kilobyte | | |size_previous |int | |ditto | | |commits_current |int | | | | |commits_previous |int | | | | |created |int | |Epoch | | |updated |int | |Epoch | | |changed |int | |Epoch | |submission | | | | | | |id |int |PK | | | |session |text | __unique 1__ | | | |url |text | __unique 1__ | index 1 | | |vcode |text |nullable | VCS.code | | |description |text |nullable | | | |email |text | |subm. email | | |submitter |text |nullable |subm. name | | |sdate |int | |epoch, index 2 | |submission_handled | | | | | | |session |text | __unique 1__ | | | |url |text | __unique 1__ | | |version_control_system | | | | | | |id |int |PK | | | |code |text |unique |Short tag | | |name |text |unique |Human Readable | |~ |~ |~ |~ |~ | |__Site Database__ | | | | | |~ |~ |~ |~ |~ | |cache_desc | | | | | | |expiry |int | |epoch | | |url |text |unique | | | |desc |text | | | |cache_url | | | | | | |expiry |int | |epoch | | |url |text |unique | | | |ok |int | | | | |resolved |text | | | |rejected | | | | | | |main.rejected | | | | |store_index | | | | | | |id |int |PK | | | |name |text | __unique 1__ |index 1 | | |vcode |text | __unique 1__ | | | |page |text | | | | |remotes |text | |index 2 | | |status |text | | | | |size_kb |int | | | | |changed |int | |epoch | | |updated |int | |epoch | | |created |int | |epoch | |submission | | | | | | |main.submission | | | | |vcs | | | | | | |main.version_control_system| | | | ## State keys and semantics |Key |Meaning | |--- |--- | |limit |State, #repos per `list` page | |start-of-current-cycle |State, epoch when update cycle started | |start-of-previous-cycle |State, epoch, previous update cycle | |store |State, path to stores on disk | |store-window-size |State, #of update durations to retain | |take |State, #repositories to update per run | |top |State, repo shown at top of `list` | |~ |~ | |mail-debug |Mail transport, debug flag | |mail-host |Mail transport, smtpd host | |mail-pass |Mail transport, smtp password | |mail-port |Mail transport, smtpd port | |mail-sender |Mail transport, smtp sender | |mail-tls |Mail transport, tls flag | |mail-user |Mail transport, smtp user | |~ |~ | |mail-footer |Mail config, footer text | |mail-header |Mail config, header text | |mail-width |Mail config, table width limit | |report-mail-destination |Mail config, destination | |~ |~ | |site-active |Site config, flag of use | |site-logo |Site config, url to logo | |site-mgr-mail |Site config, email of manager | |site-mgr-name |Site config, name of manager | |site-store |Site config, path to site on disk | |site-title |Site config, general title | |site-url |Site config, url of site | |
Deleted doc/schema.md.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Changes to lib/cli/cmdr.tcl.
︙ | ︙ | |||
201 202 203 204 205 206 207 | state th { Terminal Height. Auto supplied to all commands. } { generate [lambda {args} { linenoise lines }] } | | | | | | | | | | | 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 | state th { Terminal Height. Auto supplied to all commands. } { generate [lambda {args} { linenoise lines }] } common .optional-project { input project { The project to operate on. } { optional validate [m::cmdr::vt project] generate [m::cmdr::call glue gen_current_project] } } common .list-optional-project { input projects { Projects to operate on. } { list ; optional ; validate [m::cmdr::vt project] } } common .optional-repository { input repository { Repository to operate on. } { optional validate [m::cmdr::vt repository] |
︙ | ︙ | |||
281 282 283 284 285 286 287 | input path { New location of the store } { optional ; validate rwpath } } [m::cmdr::call glue cmd_store] private take { description { | | | | 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 | input path { New location of the store } { optional ; validate rwpath } } [m::cmdr::call glue cmd_store] private take { description { Query/change the number of repositories processed per update cycle. } input take { New number of projects to process in one update. } { optional ; validate cmdr::validate::posint } } [m::cmdr::call glue cmd_take] private report { description { Query/change the email address to send reports to. } |
︙ | ︙ | |||
380 381 382 383 384 385 386 | use .optional-repository } [m::cmdr::call glue cmd_remove] private add { use .cms description { Add repository. The new repository is placed into its own | | > > > | | | | | | | | | | | | | | | | | | | | | | | | 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 | use .optional-repository } [m::cmdr::call glue cmd_remove] private add { use .cms description { Add repository. The new repository is placed into its own project. Command tries to auto-detect vcs type if not specified. Command derives a name from the url if not specified. New repository becomes current. } option track-forks { Force tracking when seeing a large number of forks. } { presence } option vcs { Version control system handling the repository. } { validate [m::cmdr::vt vcs] generate [m::cmdr::call glue gen_vcs] } state vcs-code { Version control system handling the repository. Internal code, derived from the option value (database id). } { generate [m::cmdr::call glue gen_vcs_code] } input url { Location of the repository to add. } { validate [m::cmdr::vt url] } option name { Name for the project to hold the repository. } { alias N validate str generate [m::cmdr::call glue gen_name] } } [m::cmdr::call glue cmd_add] private rename { use .cms description { Change the name of the specified project, or the project indicated by the current repository. The rolodex does not change. } use .optional-project input name { New name for the project. } { validate str } } [m::cmdr::call glue cmd_rename] private merge { use .cms description { Merges the specified projects into a single project. When only one project is specified the set of the current repository is used as the merge target. When no projects are specified at all the projects of current and previous repositories are merged, using the prooject of current as merge target The name of the primary project becomes the name of the merge. The rolodex does not change. } use .list-optional-project } [m::cmdr::call glue cmd_merge] private split { use .cms description { Split the specified or current repository from its project. Generates a new project for the repository. The name will be derived from the original name. The referenced repository becomes current. If the referenced repository is a standalone already then nothing is done. } use .optional-repository } [m::cmdr::call glue cmd_split] private current { use .cms.nav description { Shows the rolodex. } } [m::cmdr::call glue cmd_current] alias @ private export { use .cms.ex description { Write the known set of repositories and projects to stdout, in a form suitable for (re)import. } } [m::cmdr::call glue cmd_export] private import { use .cms.ex description { Read a set of repositories and projects from the specified file, or stdin, and add them here. Ignores known repositories. Makes projects on name conflicts. Ignores projects with no repositories (including only ignored repositories). Processes the format generated by export. } option dated { Add datestamp to the generated projects. } { presence } input spec { Path to the file to read the import specification from. Falls back to stdin when no file is specified. } { optional ; validate rchan ; default stdin } } [m::cmdr::call glue cmd_import] |
︙ | ︙ | |||
510 511 512 513 514 515 516 | Swap current and previous repository } } [m::cmdr::call glue cmd_swap_current] private update { use .cms description { | | | | | | | | | | | 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 | Swap current and previous repository } } [m::cmdr::call glue cmd_swap_current] private update { use .cms description { Runs an update cycle on the specified repositories. When no repositories are specified use the next `take` number of repositories from the list of pending repositories. If no repositories are pending refill the list with the entire set of repositories and then take from the list. } use .list-optional-repository } [m::cmdr::call glue cmd_update] private updates { use .cms.in description { Show compressed history of past updates. Sorted by last changed, updated, created. Empty lines between update cycles } } [m::cmdr::call glue cmd_updates] private pending { use .cms.in description { Show list of currently pending repositories. I.e repositories waiting for an update. Order shown is the order they are taken, from the top down. } } [m::cmdr::call glue cmd_pending] private issues { use .cms.in description { Show list of active stores with issues. |
︙ | ︙ | |||
574 575 576 577 578 579 580 | alias L validate [m::cmdr::vt limit] generate [m::cmdr::call glue gen_limit] } input pattern { When specified, search for repositories matching the pattern. This is a case-insensitive substring search on | | | 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 | alias L validate [m::cmdr::vt limit] generate [m::cmdr::call glue gen_limit] } input pattern { When specified, search for repositories matching the pattern. This is a case-insensitive substring search on repository urls and project names. A search overrides and voids any and all repository and limit specifications. This also keeps the cursor unchanged. The rolodex however is filled with the search results. } { optional ; validate str } } [m::cmdr::call glue cmd_list] private reset { |
︙ | ︙ | |||
639 640 641 642 643 644 645 | state vcs-code { Version control system handling the repository. Internal code, derived from the option value (database id). } { generate [m::cmdr::call glue gen_vcs_code] } option name { | | | 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 | state vcs-code { Version control system handling the repository. Internal code, derived from the option value (database id). } { generate [m::cmdr::call glue gen_vcs_code] } option name { Name for the future project to hold the submitted repository. } { alias N validate str generate [m::cmdr::call glue gen_name] } } [m::cmdr::call glue cmd_submit] |
︙ | ︙ | |||
665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 | option vcs { Version control system handling the repository. Override the submission. } { validate [m::cmdr::vt vcs] generate [m::cmdr::call glue gen_submit_vcs] } option nomail { Disable generation and sending of acceptance mail. } { presence } state vcs-code { Version control system handling the repository. Internal code, derived from the option value (database id). } { generate [m::cmdr::call glue gen_vcs_code] } state url { Location of the repository. Taken from the submission. } { validate str generate [m::cmdr::call glue gen_submit_url] } option name { | > > > | | 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 | option vcs { Version control system handling the repository. Override the submission. } { validate [m::cmdr::vt vcs] generate [m::cmdr::call glue gen_submit_vcs] } option track-forks { Force tracking when seeing a large number of forks. } { presence } option nomail { Disable generation and sending of acceptance mail. } { presence } state vcs-code { Version control system handling the repository. Internal code, derived from the option value (database id). } { generate [m::cmdr::call glue gen_vcs_code] } state url { Location of the repository. Taken from the submission. } { validate str generate [m::cmdr::call glue gen_submit_url] } option name { Name for the project to hold the repository. Overrides the name from the submission. } { alias N validate str generate [m::cmdr::call glue gen_submit_name] } } [m::cmdr::call glue cmd_accept] |
︙ | ︙ | |||
950 951 952 953 954 955 956 | private test-vt-repo { description { Show the knowledge map used by the repository validator. } } [m::cmdr::call glue cmd_test_vt_repository] | | | | | 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 | private test-vt-repo { description { Show the knowledge map used by the repository validator. } } [m::cmdr::call glue cmd_test_vt_repository] private test-vt-project { description { Show the knowledge map used by the project validator. } } [m::cmdr::call glue cmd_test_vt_project] private test-vt-submission { description { Show the knowledge map used by the submission validator. } } [m::cmdr::call glue cmd_test_vt_submission] |
︙ | ︙ |
Changes to lib/cli/glue.tcl.
︙ | ︙ | |||
129 130 131 132 133 134 135 | debug.m/glue {[debug caller] | [$p config] } debug.m/glue {[debug caller] | --> $vcode ($vcs) } return $vcs } proc ::m::glue::gen_name {p} { debug.m/glue {[debug caller] | } | | | 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 | debug.m/glue {[debug caller] | [$p config] } debug.m/glue {[debug caller] | --> $vcode ($vcs) } return $vcs } proc ::m::glue::gen_name {p} { debug.m/glue {[debug caller] | } package require m::project package require m::vcs # Derive a name from the url when no such was specified by the # user. Add a serial number if that name is already in use. set name [MakeName \ [m vcs name-from-url \ [$p config @vcs-code] \ |
︙ | ︙ | |||
187 188 189 190 191 192 193 | debug.m/glue {[debug caller] | [$p config] } debug.m/glue {[debug caller] | undefined } $p undefined! # Will not reach here } | | | | | | | | | 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 | debug.m/glue {[debug caller] | [$p config] } debug.m/glue {[debug caller] | undefined } $p undefined! # Will not reach here } proc ::m::glue::gen_current_project {p} { debug.m/glue {[debug caller] | } # Provide current as project for operation when not specified # by the user. Fail if we have no current repository to trace # from. package require m::repo package require m::rolodex # set r [m rolodex top] if {$r ne {}} { set project [m repo project $r] if {$project ne {}} { debug.m/glue {[debug caller] | --> $project } return $project } } debug.m/glue {[debug caller] | [$p config] } debug.m/glue {[debug caller] | undefined } $p undefined! # Will not reach here } # # ## ### ##### ######## ############# ###################### proc ::m::glue::cmd_import {config} { debug.m/glue {[debug caller] | } package require m::project package require m::repo package require m::rolodex package require m::store package require m::url set dated [$config @dated] if {[$config @spec set?]} { |
︙ | ︙ | |||
238 239 240 241 242 243 244 | [ImportRead $sname [$config @spec]]]] SiteRegen OK } proc ::m::glue::cmd_export {config} { debug.m/glue {[debug caller] | } | | | | 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 | [ImportRead $sname [$config @spec]]]] SiteRegen OK } proc ::m::glue::cmd_export {config} { debug.m/glue {[debug caller] | } package require m::project package require m::repo m msg [m project spec] } proc ::m::glue::cmd_reply_add {config} { debug.m/glue {[debug caller] | } package require m::db package require m::reply |
︙ | ︙ | |||
359 360 361 362 363 364 365 | }] show } OK } proc ::m::glue::cmd_show {config} { debug.m/glue {[debug caller] | } | | | | 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 | }] show } OK } proc ::m::glue::cmd_show {config} { debug.m/glue {[debug caller] | } package require m::repo package require m::state set all [$config @all] m db transaction { set n [m state limit] if {!$n} { set n [color note {adjust to terminal height}] } [table/d t { $t add Store [m state store] $t add Limit $n $t add Take "[m state take] ([m repo count-pending] pending/[m repo count] total)" $t add Window [m state store-window-size] $t add {Report To} [m state report-mail-destination] $t add {-} {} $t add {Cycle, Last} [m format epoch [m state start-of-previous-cycle]] $t add {Cycle, Now} [m format epoch [m state start-of-current-cycle]] if {$all} { |
︙ | ︙ | |||
528 529 530 531 532 533 534 | if {[$config @take set?]} { m state take [$config @take] } set n [m state take] } | | | 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 | if {[$config @take set?]} { m state take [$config @take] } set n [m state take] } set g [expr {$n == 1 ? "project" : "projects"}] m msg "Per update, take [color note $n] $g" OK } proc ::m::glue::cmd_window {config} { debug.m/glue {[debug caller] | } package require m::state |
︙ | ︙ | |||
582 583 584 585 586 587 588 | } }] show OK } proc ::m::glue::cmd_add {config} { debug.m/glue {[debug caller] | } | | | < | | | | | < > | | | > | < | > | < | | > > > | > | > | | > > > | 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 | } }] show OK } proc ::m::glue::cmd_add {config} { debug.m/glue {[debug caller] | } package require m::project package require m::repo package require m::rolodex package require m::store m db transaction { Add $config } ShowCurrent $config SiteRegen OK } proc ::m::glue::cmd_remove {config} { debug.m/glue {[debug caller] | } package require m::project package require m::repo package require m::rolodex package require m::store m db transaction { set repo [$config @repository] set rinfo [m repo get $repo] dict with rinfo {} # -> url : repo url # vcs : vcs id # -> vcode : vcs code # -> project: project id # -> name : project name # -> store : id of backing store for repo m msg "Removing $vcode repository [color note $url] ..." m msg "from Project [color note $name]" m repo remove $repo set siblings [m store remotes $store] set nsiblings [llength $siblings] if {!$nsiblings} { m msg "- Removing unshared $vcode store $store ..." m store remove $store } else { set x [expr {($nsiblings == 1) ? "repository" : "repositories"}] m msg "- Keeping $vcode store $store still used by $nsiblings $x" } # Remove project if no repositories remain at all. set nsiblings [m project size $project] if {!$nsiblings} { m msg "- Removing now empty project ..." m project remove $project } else { set x [expr {($nsiblings == 1) ? "repository" : "repositories"}] m msg "- Keeping project still used by $nsiblings $x" } m rolodex drop $repo m rolodex commit } ShowCurrent $config |
︙ | ︙ | |||
654 655 656 657 658 659 660 | set line [string range $line 0 ${w}-5]... } lappend r $line } join $r \n } | > > > > > > > > > > > > | | | | < > > | > | | | | | | < > > > > | < < < < > | > | > > > > | | < | | | | < < < < > > > > > > > > > > > > > > > > > > > > > | > > > > > | > > | > | | > | | | | | | | | < < < < < < < < < < < < < < < < < < < < < < | | > > > | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | | | | > | | | | | | | | | | | < | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 | set line [string range $line 0 ${w}-5]... } lappend r $line } join $r \n } proc m::glue::Short {repo} { set ri [m repo get $repo] dict with ri {} set active [color {*}[dict get { 0 {warning offline} 1 {note UP} } [expr {!!$active}]]] return "$url ([SIB [expr {!$issues}]] $active)" } proc ::m::glue::cmd_details {config} { ;# XXX REWORK due the project/repo/store relation changes debug.m/glue {[debug caller] | } package require m::project package require m::repo package require m::rolodex package require m::state package require m::store package require linenoise set w [$config @tw] ;#linenoise columns # table/d -> 2 columns, 7 overhead, 1st col 14 wide => set w [expr {$w - 21}] ;# max width for col 2. m db transaction { set full [$config @full] set repo [$config @repository] # Basic repository details ........................... set rinfo [m repo get $repo] dict with rinfo {} #m msg "Details of [color note $url] ..." # -> url : repo url # active : usage state # vcs : vcs id # vcode : vcs code # * project: project id # * name : project name # -> store : id of backing store for repo # min_sec: minimal time spent on setup/update # max_sec: maximal time spent on setup/update # win_sec: last n times for setup/update # checked: epoch of last check # origin : repository this is forked from, or empty set spent [StatsTime $min_sec $max_sec $win_sec] # Get store details ... set path [m store path $store] set sd [m store get $store] dict unset sd vcs dict unset sd min_sec dict unset sd max_sec dict unset sd win_sec dict with sd {} # size, sizep # commits, commitp # vcsname # created # changed # updated lassign [m vcs caps $store] stdout stderr set stdout [string trim $stdout] set stderr [string trim $stderr] # Find repositories sharing the store ................ set storesibs [m store repos $store] # Find repositories which are siblings of the same origin set forksibs {} set dorigin {} if {$origin ne {}} { set forksibs [m repo forks $origin] set dorigin [Short $origin] } # Find repositories which are siblings of the same project set projectsibs [m repo for $project] #puts O(($origin))/\nR(($repo))/\nS(($storesibs))/\nF(($forksibs))/\nP(($projectsibs)) # Compute derived information ... set status [SI $stderr] set export [m vcs export $vcs $store] set dcommit [DeltaCommitFull $commits $commitp] set dsize [DeltaSizeFull $size $sizep] set changed [color note [m format epoch $changed]] set updated [m format epoch $updated] set created [m format epoch $created] set active [color {*}[dict get { 0 {warning offline} 1 {note UP} } [expr {!!$active}]]] set s [[table/d s { $s borders 0 set sibs 0 foreach sibling $storesibs { if {$sibling == $repo} continue incr sibs $s add ${sibs}. [Short $sibling] } if {$sibs} { $s add {} {} } $s add Size $dsize $s add Commits $dcommit if {$export ne {}} { $s add Export $export } $s add {Update Stats} $spent $s add {Last Change} $changed $s add {Last Check} $updated $s add Created $created if {!$full} { set nelines [llength [split $stderr \n]] set nllines [llength [split $stdout \n]] if {$nelines == 0} { set nelines [color note {no log}] } if {$nllines == 0} { set nllines [color note {no log}] } $s add Operation $nllines if {$stderr ne {}} { $s add "Notes & Errors" [color bad $nelines] } else { $s add "Notes & Errors" $nelines } } else { if {$stdout ne {}} { $s add Operation [L $stdout] } if {$stderr ne {}} { $s add "Notes & Errors" [L $stderr] } } }] show return] [table/d t { $t add {} [color note $url] if {$origin ne {}} { $t add Origin $dorigin } $t add Status "$status $active @[color note [m format epoch $checked]]" $t add Project $name $t add VCS $vcsname $t add {Local Store} $path $t add {} $s # Show other locations serving the project, except for forks. # Forks are shown separately. set sibs 0 foreach sibling $projectsibs { if {$sibling == $repo} continue if {$sibling == $origin} continue if {$sibling in $storesibs} continue if {$sibling in $forksibs} continue if {!$sibs} { $t add Other {} } incr sibs $t add ${sibs}. [Short $sibling] } set threshold 20 # Show the sibling forks. Only the first, only if not sharing the store. set sibs 0 foreach sibling $forksibs { if {$sibling == $repo} continue if {$sibling == $origin} continue if {$sibling in $storesibs} continue if {!$sibs} { $t add Related {} } incr sibs # if {$sibs > $threshold} continue $t add ${sibs}. [Short $sibling] } if {$sibs > $threshold} { $t add {} "(+[expr {$sibs - $threshold}] more)" } }] show } OK } proc ::m::glue::SI {stderr} { SIB [expr {$stderr eq {}}] } proc ::m::glue::SIB {ok} { color {*}[dict get { 0 {bad ATTEND} 1 {good OK} } $ok] } proc ::m::glue::cmd_enable {flag config} { debug.m/glue {[debug caller] | } package require m::project package require m::repo package require m::rolodex package require m::store set op [expr {$flag ? "Enabling" : "Disabling"}] m db transaction { foreach repo [$config @repositories] { m msg "$op [color note [m repo name $repo]] ..." set rinfo [m repo get $repo] dict with rinfo {} # -> url : repo url # vcs : vcs id # vcode : vcs code # project: project id # name : project name # store : id of backing store for repo m repo enable $repo $flag # Note: We do not manipulate `repo_pending`. An existing # repo is always in `repo_pending`, even if it is # inactive. The commands to retrieve the pending repos # (all, or taken for update) is where we do the filtering, # i.e. exclusion of the inactive. } } ShowCurrent $config SiteRegen OK } proc ::m::glue::cmd_rename {config} { debug.m/glue {[debug caller] | } package require m::project package require m::store m db transaction { set project [$config @project] ; debug.m/glue {project : $project} set newname [$config @name] ; debug.m/glue {new name: $newname} set oldname [m project name $project] m msg "Renaming [color note $oldname] ..." if {$newname eq $oldname} { m::cmdr::error \ "The new name is the same as the current name." \ NOP } if {[m project has $newname]} { m::cmdr::error \ "New name [color note $newname] already present" \ HAVE_ALREADY NAME } Rename $project $newname } ShowCurrent $config SiteRegen OK } proc ::m::glue::cmd_merge {config} { debug.m/glue {[debug caller] | } package require m::project package require m::repo package require m::rolodex package require m::store package require m::vcs m db transaction { set msets [Dedup [MergeFill [$config @projects]]] # __Attention__: Cannot place the mergefill into a generate # clause, the parameter logic is too simple (set / not set) to # handle the case of `set only one`. debug.m/glue {msets = ($msets)} if {[llength $msets] < 2} { m::cmdr::error \ "All repositories are already in the same project." \ NOP } set secondaries [lassign $msets primary] m msg "Target: [color note [m project name $primary]]" foreach secondary $secondaries { m msg "Merging: [color note [m project name $secondary]]" Merge $primary $secondary } } ShowCurrent $config SiteRegen OK } proc ::m::glue::cmd_split {config} { debug.m/glue {[debug caller] | } package require m::project package require m::repo package require m::rolodex package require m::store package require m::vcs m db transaction { set repo [$config @repository] set rinfo [m repo get $repo] dict with rinfo {} # -> url : repo url # vcs : vcs id # vcode : vcs code # project: project id # name : project name # store : id of backing store for repo m msg "Attempting to separate" m msg " Repository [color note $url]" m msg " Managed by [color note [m vcs name $vcs]]" m msg "From" m msg " Project [color note $name]" if {[m project size $mset] < 2} { m::cmdr::error \ "The project is to small for splitting" \ ATOMIC } set newname [MakeName $name] set projectnew [m project add $newname] m msg "New" m msg " Project [color note $newname]" m repo move/1 $repo $projectnew if {![m project has-vcs $mset $vcs]} { # The moved repository was the last user of its vcs in the # original project. We can simply move its store over # to the new holder to be ok. m msg " Move store ..." m store move $store $projectnew } else { # The originating mset still has users for the store used # by the moved repo. Need a new store for the moved repo. m msg " Split store ..." m store cleave $store $projectnew } } ShowCurrent $config SiteRegen OK } |
︙ | ︙ | |||
982 983 984 985 986 987 988 | ShowCurrent $config OK } proc ::m::glue::cmd_update {config} { debug.m/glue {[debug caller] | } | | > | | > | | | < > > > < | > > > > > | < > > | | | < < > | < | | | < | | > > > | < < < | > > | < < | | | > | | | | | | | | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > | > > | | | | | | | | > > > | | > > | | | | | | < | | < < | | | | | | | | | > > | | | 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 | ShowCurrent $config OK } proc ::m::glue::cmd_update {config} { debug.m/glue {[debug caller] | } package require m::project package require m::state package require m::store package require struct::set set startcycle [m state start-of-current-cycle] set nowcycle [clock seconds] m db transaction { set verbose [$config @verbose] set repos [UpdateRepos $startcycle $nowcycle [$config @repositories]] debug.m/glue {repositories = ($repos)} foreach repo $repos { set ri [m repo get $repo] dict with ri {} # url, active, issues, vcs, vcode, project, name, store # min/max/win_sec, checked, origin set si [m store get $store] # size, vcs, sizep, commits, commitp, vcsname, updated, changed, created # (atted, min/max/win, remote, active) set before [dict get $si commits] set durl [color note $url] if {$origin eq {}} { set durl [color bg-cyan $durl] } m msg "Updating repository $durl ..." m msg "In project [color note $name]" if {$verbose} { m msg " [color note [string totitle $vcode]] store ... " } else { m msg* " [string totitle $vcode] store ... " } set primary [expr {$origin eq {}}] # -- has_issues, is_active/enable -- fork handling set now [clock seconds] lassign [m store update $primary $url $store $nowcycle $now $before] \ ok duration commits size forks set attend [expr {!$ok || [m store has-issues $store]}] set suffix ", in [color note [m format interval $duration]]" m repo times $repo $duration $now $attend if {!$primary && $attend} { m repo enable $repo 0 } if {!$ok} { lassign [m vcs caps $store] _ e m msg "[color bad Fail]$suffix" m msg $e continue } elseif {$before != $commits} { set delta [expr {$commits - $before}] if {$delta < 0} { set mark bad } else { set mark note set delta +$delta } m msg "[color note Changed] $before $commits ([color $mark $delta])$suffix" } elseif {$verbose} { m msg "[color note "No changes"]$suffix" } else { m msg "No changes$suffix" } if {$primary} { # check currently found forks against what is claimed by the system set forks_prev [m repo fork-locations $repo] lassign [struct::set intersect3 $forks_prev $forks] same removed added # previous - current => removed from previous # current - previous => added over previous # Actions: # - The removed forks are detached from the primary. # We keep the repository. Activation state is unchanged # # - Unchanged forks are reactivated if they got disabled. # # - New forks are attempted to be added back # This may actually reclaim a fork which was declaimed before. # # Note: Only these new forks have to be validated! # Note: Tracking threshold is irrelevant here. foreach r $removed { m msg " [color warning {Detaching lost}] [color note $r]" m repo declaim [m repo id $r] } foreach r $same { # m msg " Unchanged [color note $r], activating" m repo enable [m repo id $r] } AddForks $added $repo $vcs $vcode $name $project } } } SiteRegen OK } proc ::m::glue::cmd_updates {config} { debug.m/glue {[debug caller] | } package require m::store m db transaction { # m store updates XXX rework actually repos # TODO: get status (stderr), show - store id set series {} foreach row [TruncH [m store updates] [expr {[$config @th]-1}]] { if {[lindex $row 0] eq "..."} { lappend series [list ... {} {} {} {} {} {}] continue } # store mname vcode changed updated created size active # remote sizep commits commitp mins maxs lastn url origin dict with row {} if {$created eq "."} { lappend series [list - - - - - - -] continue } set changed [m format epoch $changed] set updated [m format epoch $updated] set created [m format epoch $created] set dsize [DeltaSize $size $sizep] set dcommit [DeltaCommit $commits $commitp] set lastn [LastTime $lastn] if {$origin eq {}} { set url [color bg-cyan $url] } lappend series [list $url $vcode $dsize $dcommit $lastn $changed $updated $created] } } lassign [TruncW \ {Project VCS Size Commits Time Changed Updated Created} \ {1 0 0 0 0 0 0 0} \ $series [$config @tw]] \ titles series m msg "Cycles: [m format epoch [m state start-of-previous-cycle]] ... [m format epoch [m state start-of-current-cycle]] ..." [table t $titles { foreach row $series { $t add {*}$row } }] show OK } proc ::m::glue::cmd_pending {config} { debug.m/glue {[debug caller] | } package require m::project package require m::state set tw [$config @tw] set th [$config @th] ; incr th -1 ;# Additional line before the table (counts). set nrepo [m repo count] set npending [m repo count-pending] m db transaction { set series {} set take [m state take] foreach {pname url origin nforks} [m repo pending] { if {$origin eq {}} { set url [color bg-cyan $url] } set row {} if {$take} { lappend row * incr take -1 } else { lappend row {} } lappend row $url $nforks $pname lappend series $row } } lassign [TruncW \ {{} Repository Forks Project} \ {0 0 0 1} \ [TruncH $series $th] $tw] \ titles series puts @[color note $npending]/$nrepo [table t $titles { foreach row $series { $t add {*}$row } }] show OK } proc ::m::glue::cmd_issues {config} { debug.m/glue {[debug caller] | } package require m::project package require m::repo package require m::rolodex package require m::store m db transaction { set series {} foreach row [m store issues] { ;# XXX rework actually repo issues dict with row {} # store mname vcode changed updated created size active remote rid url set size [m format size $size] if {$origin eq {}} { set url [color bg-cyan $url] } lappend series [list $rid $url $mname $vcode $size] m rolodex push $rid } m rolodex commit set n [llength $series] set table {} foreach row $series { incr n -1 set row [lassign $row rid] set dex [m rolodex id $rid] set tag @$dex if {$n == 1} { lappend tag @p } if {$n == 0} { lappend tag @c } lappend table [list $tag {*}$row] } } lassign [TruncW \ {Tag Repository Project VCS Size} \ {0 0 1 0 0} \ $table [$config @tw]] \ titles series [table t $titles { foreach row $series { $t add {*}[C $row 1 note] ;# 1 => url } }] show OK } proc ::m::glue::cmd_disabled {config} { debug.m/glue {[debug caller] | } package require m::project package require m::repo package require m::rolodex package require m::store m db transaction { set series {} foreach row [m store disabled] { # XXX REWORK actually repo state dict with row {} # store mname vcode changed updated created size active remote attend rid url origin set size [m format size $size] if {$origin eq {}} { set url [color bg-cyan $url] } lappend series [list $rid $url $mname $vcode $size] m rolodex push $rid } m rolodex commit set n [llength $series] set table {} foreach row $series { incr n -1 set row [lassign $row rid] set dex [m rolodex id $rid] set tag @$dex if {$n == 1} { lappend tag @p } if {$n == 0} { lappend tag @c } lappend table [list $tag {*}$row] } } lassign [TruncW \ {Tag Repository Project VCS Size} \ {0 0 1 0 0} \ $table [$config @tw]] \ titles series [table t $titles { foreach row $series { $t add {*}[C $row 1 note] ;# 1 => url } }] show |
︙ | ︙ | |||
1245 1246 1247 1248 1249 1250 1251 1252 1253 | set series [m repo search $pattern] } else { # No search, show a chunk of the list as per options. if {[$config @repository set?]} { set repo [$config @repository] set ri [m repo get $repo] dict with ri {} set first [list $name $url] debug.m/glue {from request: $first} | > > > > > > | | | | > > | | | | 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 | set series [m repo search $pattern] } else { # No search, show a chunk of the list as per options. if {[$config @repository set?]} { set repo [$config @repository] set ri [m repo get $repo] dict with ri {} # -> url : repo url # vcs : vcs id # vcode : vcs code # project: project id # -> name : project name # store : id of backing store for repo set first [list $name $url] debug.m/glue {from request: $first} unset name url vcs vcode store ri project } else { set first [m state top] debug.m/glue {from state: $first} } set limit [$config @limit] if {$limit == 0} { set limit [expr {[$p config @th]-7}] } lassign [m repo get-n $first $limit] next series debug.m/glue {next ($next)} m state top $next } # series = list (dict (primary name url rid vcode sizekb active sizep commits commitp mins maxs lastn)) debug.m/glue {series ($series)} set n 0 foreach row $series { m rolodex push [dict get $row id] incr n } set idx -1 set table {} foreach row $series { dict with row {} # primary name url id vcode sizekb active sizep commits commitp mins maxs lastn incr idx #set url [color note $url] set ix [m rolodex id $id] set tag {} if {$ix ne {}} { lappend tag @$ix } if {$idx == ($n-2)} { lappend tag @p } if {$idx == ($n-1)} { lappend tag @c } set a [expr {$active ? "A" : "-"}] if {$primary} { set url [color bg-cyan $url] } set dsize [DeltaSize $sizekb $sizep] set dcommit [DeltaCommit $commits $commitp] set lastn [LastTime $lastn] lappend table [list $tag $a $url $name $vcode $dsize $dcommit $lastn] # ................. 0 1 2 3 4 5 6 7 } } # See also ShowCurrent # TODO: extend list with store times ? lassign [TruncW \ {Tag {} Repository Project VCS Size Commits Time} \ {0 0 0 1 0 -1 -1 0} \ $table [$config @tw]] \ titles series [table t $titles { foreach row $series { $t add {*}[C $row 2 note] ;# 2 => url } }] show |
︙ | ︙ | |||
1367 1368 1369 1370 1371 1372 1373 | set when [m format epoch $when] lappend series [list $id $when $url $vcode $desc $email $submitter] } } lassign [TruncW \ {{} When Url VCS Description Email Submitter} \ | | | 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 | set when [m format epoch $when] lappend series [list $id $when $url $vcode $desc $email $submitter] } } lassign [TruncW \ {{} When Url VCS Description Email Submitter} \ {0 0 0 0 3 0 1} \ $series [$config @tw]] \ titles series [table t $titles { foreach row $series { $t add {*}[C $row 2 note] ;# 2 => url } }] show |
︙ | ︙ | |||
1390 1391 1392 1393 1394 1395 1396 | set series {} foreach {url reason} [m submission rejected] { lappend series [list $url $reason] } } lassign [TruncW \ {Url Reason} \ | | | | 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 | set series {} foreach {url reason} [m submission rejected] { lappend series [list $url $reason] } } lassign [TruncW \ {Url Reason} \ {1 0} \ $series [$config @tw]] \ titles series [table t $titles { foreach row $series { $t add {*}[C $row 0 note] ;# 0 => url } }] show OK } proc ::m::glue::cmd_submit {config} { debug.m/glue {[debug caller] | } package require m::submission package require m::repo # session id for cli, daily rollover, keyed to host and user set sid "cli.[expr {[clock second] % 86400}]/[info hostname]/$::tcl_platform(user)" m db transaction { set url [Url $config] set email [$config @email] set submitter [$config @submitter] set vcode [$config @vcs-code] set desc [$config @name] set url [m vcs url-norm $vcode $url] |
︙ | ︙ | |||
1450 1451 1452 1453 1454 1455 1456 | } SiteRegen OK } proc ::m::glue::cmd_accept {config} { debug.m/glue {[debug caller] | } | | | 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 | } SiteRegen OK } proc ::m::glue::cmd_accept {config} { debug.m/glue {[debug caller] | } package require m::project package require m::repo package require m::rolodex package require m::store package require m::submission package require m::mail::generator package require m::mailer |
︙ | ︙ | |||
1605 1606 1607 1608 1609 1610 1611 | m mailer to [$config @destination] [m mail generator reply $message {}] } else { m db transaction { set message [ComeAroundMail [$config @tw] [m state start-of-current-cycle] [clock seconds]] } m msg $message } | | > | > > > > | | | | | | | | | 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 | m mailer to [$config @destination] [m mail generator reply $message {}] } else { m db transaction { set message [ComeAroundMail [$config @tw] [m state start-of-current-cycle] [clock seconds]] } m msg $message } OK } proc ::m::glue::cmd_test_mail_config {config} { debug.m/glue {[debug caller] | } package require m::mailer package require m::mail::generator try { m mailer to [$config @destination] [m mail generator test] } on error {e o} { m msg [color bad $e] exit } OK } proc ::m::glue::cmd_test_vt_repository {config} { debug.m/glue {[debug caller] | } package require m::repo set map [m repo known] [table/d t { foreach k [lsort -dict [dict keys $map]] { set v [dict get $map $k] $t add $v $k } }] show OK } proc ::m::glue::cmd_test_vt_project {config} { debug.m/glue {[debug caller] | } package require m::project set map [m project known] [table/d t { foreach k [lsort -dict [dict keys $map]] { set v [dict get $map $k] $t add $v $k } }] show OK } proc ::m::glue::cmd_test_vt_reply {config} { debug.m/glue {[debug caller] | } package require m::reply set map [m reply known] [table/d t { foreach k [lsort -dict [dict keys $map]] { set v [dict get $map $k] $t add $v $k } }] show OK } proc ::m::glue::cmd_test_vt_submission {config} { debug.m/glue {[debug caller] | } package require m::submission set map [m submission known] [table/d t { foreach k [lsort -dict [dict keys $map]] { set v [dict get $map $k] $t add $v $k } }] show OK } proc ::m::glue::cmd_debug_levels {config} { debug.m/glue {[debug caller] | } |
︙ | ︙ | |||
1741 1742 1743 1744 1745 1746 1747 | } proc ::m::glue::ImportRead {label chan} { debug.m/glue {[debug caller] | } m msg "Reading [color note $label] ..." return [split [string trim [read $chan]] \n] # :: list (command) | | > | > | 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 | } proc ::m::glue::ImportRead {label chan} { debug.m/glue {[debug caller] | } m msg "Reading [color note $label] ..." return [split [string trim [read $chan]] \n] # :: list (command) # command :: list ('M' name) - old: 'M'irrorset # | list ('P' name) - new: 'P'roject # | list ('R' vcode url) } proc ::m::glue::ImportVerify {commands} { debug.m/glue {} # commands :: list (command) # command :: list ('M' name) - old: 'M'irrorset # | list ('P' name) - new: 'P'roject # | list ('R' vcode url) m msg "Verifying ..." foreach {code name} [m vcs all] { dict set vcs $code . dict set vcs $name . |
︙ | ︙ | |||
1776 1777 1778 1779 1780 1781 1782 | set command [string trim $command] # skip empty lines if {$command eq {}} continue lassign $command cmd a b switch -exact -- $cmd { | | > | 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 | set command [string trim $command] # skip empty lines if {$command eq {}} continue lassign $command cmd a b switch -exact -- $cmd { M - P { Ping " $command" # M name --> a = name, b = ((empty string)) if {[llength $command] != 2} { lappend msg "Line [format $lfmt $lno]: Bad syntax: $command" } } R { |
︙ | ︙ | |||
1871 1872 1873 1874 1875 1876 1877 | dict set seen $resolved $lno lappend x $resolved lappend repo $vcs $resolved } M { if {![llength $repo]} { | | | 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 | dict set seen $resolved $lno lappend x $resolved lappend repo $vcs $resolved } M { if {![llength $repo]} { m msg "Line $lno: [color warning Skip] empty project [color note $vcs]" set repo {} continue } foreach r $x { dict lappend seen $r $vcs } ;# vcs = mname unset x lappend command $repo |
︙ | ︙ | |||
1924 1925 1926 1927 1928 1929 1930 | return } proc ::m::glue::Import1 {date mname repos} { debug.m/glue {[debug caller] | } # repos = list (vcode url ...) | | | | | | | | | | | | | | | | | | | 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 | return } proc ::m::glue::Import1 {date mname repos} { debug.m/glue {[debug caller] | } # repos = list (vcode url ...) m msg "Handling project [color note $mname] ..." if {[llength $repos] == 2} { lassign $repos vcode url # The project contains only a single repository. # We might be able to skip the merge if {![m project has $mname]} { # No project of the given name exists. # Create directly in final form. Skip merge. try { ImportMake1 $vcode $url $mname } trap {M VCS CHILD} {e o} { # Revert creation of mset and repository set repo [m rolodex top] set mset [m repo mset $repo] m repo remove $repo m rolodex drop $repo m project remove $mset m msg "[color bad {Unable to import}] [color note $mname]: $e" # No rethrow, the error in the child is not an error # for the whole command. Continue importing the remainder. } return } } # More than a single repository in this set, or the destination # project exists. Merging is needed. And the untrusted nature # of the input means that we cannot be sure that merging is even # allowed. # Two phases: # - Create the repositories. Each in its own project, like for # `add`. Project names are of the form `import_<date>`, plus a # serial number. Comes with associated store. # # - Go over the repositories again and merge them. If a # repository is rejected by the merge keep it separate. Retry # merging using the rejections. The number of retries is finite # because each round finalizes at least one project and its # repositories of the finite supply. At the end of this phase we # have one or more projects each with maximally merged # repositories. Each finalized project is renamed to final form, # based on the incoming mname and date. set serial 0 set r {} foreach {vcode url} $repos { try { set tmpname import${date}_[incr serial] set data [ImportMake1 $vcode $url $tmpname] dict set r $url $data } trap {M VCS CHILD} {e o} { # Revert creation of mset and repository set repo [m rolodex top] set mset [m repo mset $repo] m repo remove $repo m rolodex drop $repo m project remove $mset m msg "[color bad {Unable to use}] [color note $url]: $e\n" # No rethrow, the error in the child is not an error # for the whole command. Continue importing the remainder. } } if {![dict size $r]} { # All inputs fail, report and continue with the remainder m msg "[color bad {Unable to import}] [color note $mname]: No repositories" return } set rename 1 if {[m project has $mname]} { # Targeted project exists. Make it first in the merge list. set mset [m project id $mname] set repos [linsert $repos 0 dummy_vcode @$mname] dict set r @$mname [list dummy_vcs $mset dummy_store] set rename 0 } while on { set remainder [lassign $repos v u] |
︙ | ︙ | |||
2037 2038 2039 2040 2041 2042 2043 | Rename $msetp [MakeName $mname] set rename 0 } if {![llength $unmatched]} break # Retry to merge the leftovers. Note, each iteration | | | | > > | > > | | | | > | | | | | > | | | | > | > > | < > > > < > > > | | > > > | | > | > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > | 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 | Rename $msetp [MakeName $mname] set rename 0 } if {![llength $unmatched]} break # Retry to merge the leftovers. Note, each iteration # finalizes at least one project, ensuring termination of the # loop. set repos $unmatched set rename 1 } m rolodex commit return } proc ::m::glue::ImportMake1 {vcode url base} { debug.m/glue {[debug caller] | } set vcs [m vcs id $vcode] set tmpname [MakeName $base] set project [m project add $tmpname] set url [m vcs url-norm $vcode $url] set vcode [m vcs code $vcs] m msg "> [string totitle $vcode] repository [color note $url]" # ----------------------- # vcs project url lassign [AddStoreRepo $vcs $vcode $tmpname $url $project] repo forks set store [m repo store $repo] # Forks are not processed. It is expected that forks are in the import file. # The next update of the primary will link them to the origin. set nforks [llength $forks] if {$nforks} { m msg " [color warning "Forks found ($nforks), ignored"]" } m rolodex push $repo return [list $vcs $project $store] } proc ::m::glue::Add {config} { debug.m/glue {[debug caller] | } set url [Url $config] set vcs [$config @vcs] set vcode [$config @vcs-code] set name [$config @name] set url [m vcs url-norm $vcode $url] # __Attention__: Cannot move the url normalization into a # when-set clause of the parameter. That generates a # dependency cycle: # # url <- vcode <- vcs <- url m msg "Attempting to add" m msg " Repository [color note $url]" m msg " Managed by [color note [m vcs name $vcs]]" m msg "New" m msg " Project [color note $name]" if {[m repo has $url]} { m::cmdr::error \ "Repository already present" \ HAVE_ALREADY REPOSITORY } if 0 {if {[m project has $name]} { m::cmdr::error \ "Name already present" \ HAVE_ALREADY NAME }} # Relevant entities # 1. repository # 2. store # 3. project # # As the repository references the other two these have to be initialized first. # The creation of the repository caps the process. # Issues roll database changes back. m msg "Actions ..." # ---------------------------------- Project if {![m project has $name]} { m msg* " Setting up the project ... " set project [m project add $name] OKx } else { m msg " Project is known" } lassign [AddStoreRepo $vcs $vcode $name $url $project] repo forks # ---------------------------------- Forks if {$forks ne {}} { set threshold 22 set nforks [llength $forks] m msg "Found [color note $nforks] forks to track." if {![$config @track-forks] && ($nforks > $threshold)} { m msg [color warning "Auto-tracking threshold of $threshold forks exceeded"] m::cmdr::error "Please confirm using [color note --track-forks] that this many forks should be tracked." \ TRACKING-THRESHOLD } AddForks $forks $repo $vcs $vcode $name $project } # ---------------------------------- m msg "Setting new primary as current repository" m rolodex push $repo m rolodex commit return } proc ::m::glue::AddForks {forks repo vcs vcode name project} { debug.m/glue {[debug caller] | } set nforks [llength $forks] set format %-[string length $nforks]d set pad [string repeat " " [expr {3+[string length $nforks]}]] foreach fork $forks { incr k m msg " [color cyan "([format $format $k])"] Fork [color note $fork] ... " if {[m repo has $fork]} { m msg " $pad[color note "Already known, claiming it"]" # NOTE: The fork exists in a different project. We # leave that part alone. The ERD allows that, a fork # and its origin do not have to be in the same # project. m repo claim $repo [m repo id $fork] continue } # Note: Fork urls have to be validated, same as the primary location. if {![m url ok $fork xr]} { m msg " [color warning {Not reachable}], might be private or gone" m msg " Ignored" continue } AddStoreRepo $vcs $vcode $name $fork $project $repo } return } proc ::m::glue::AddStoreRepo {vcs vcode name url project {origin {}}} { debug.m/glue {[debug caller] | } # ---------------------------------- Store m msg* " Setting up the $vcode store ... " lassign [m store add $vcs $name $url] \ store duration commits size forks # id seconds int int list(url) set x [expr {($commits == 1) ? "commit" : "commits"}] m msg "[color good OK] in [color note [m format interval $duration]] ($commits $x, $size KB)" # ---------------------------------- Repository m msg* " Creating repository ... " set repo [m repo add $vcs $project $store $url $duration $origin] OKx return [list $repo $forks] } proc ::m::glue::InvalE {label key} { set v [m state $key] return [list [Inval $label {$v ne {}}] $v] } proc ::m::glue::Inval {x isvalid} { |
︙ | ︙ | |||
2154 2155 2156 2157 2158 2159 2160 | if {$n} { set id -1 set series {} foreach r $rolodex { incr id set rinfo [m repo get $r] dict with rinfo {} | | | | | | | | | > > > > > | | | | | | | | | | | | | | | | | | | | | 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 | if {$n} { set id -1 set series {} foreach r $rolodex { incr id set rinfo [m repo get $r] dict with rinfo {} # -> url : repo url # vcs : vcs id # -> vcode : vcs code # project: project id # -> name : project name # store : id of backing store for repo lappend tag @$id if {$id == ($n-2)} { lappend tag @p } if {$id == ($n-1)} { lappend tag @c } lappend series [list $tag $url $name $vcode] unset tag } } } if {$n} { lassign [TruncW \ {{} {} {} {}} \ {0 1 3 0} \ $series [$config @tw]] \ titles series [table t $titles { $t borders 0 $t headers 0 foreach row $series { $t add {*}[C $row 1 note] ;# 1 => url } }] show } return } proc ::m::glue::OK {} { debug.m/glue {[debug caller] | } m msg [color good OK] return -code return } proc ::m::glue::OKx {} { debug.m/glue {[debug caller] | } m msg [color good OK] } proc ::m::glue::MakeName {prefix} { debug.m/glue {[debug caller] | } if {![m project has $prefix]} { return $prefix } set n 1 while {[m project has ${prefix}#$n]} { incr n } return "${prefix}#$n" } proc ::m::glue::ComeAroundMail {width current newcycle} { debug.m/glue {[debug caller] | } package require m::db package require m::state package require m::store package require m::format # Get updates and convert into a series for the table. A series we # can compress width-wise before formatting. set series {} foreach row [m store updates] { dict with row {} # store mname vcode changed updated created size active remote # sizep commits commitp mins maxs lastn if {$created eq "."} continue ;# ignore separations if {$changed < $current} continue ;# older cycle set dcommit [DeltaCommit $commits $commitp] set dsize [DeltaSize $size $sizep] set changed [m format epoch/short $changed] set spent [LastTime $lastn] lappend series [list $changed $vcode $mname $spent $dsize $dcommit] } lappend mail "\[[info hostname]\] Cycle Report." lappend mail "Cycle\nFrom [clock format $current]\nTo [clock format $newcycle]" set n [llength $series] if {!$n} { lappend mail "Found no changes." } else { lappend mail "Found @/n/@ changed repositories:\n" lassign [TruncW \ {Changed VCS Project Time Size Commits} \ {0 0 1 0 0 0} \ $series \ $width] \ titles series table t $titles { foreach row $series { $t add {*}$row } } lappend mail [$t show return] $t destroy } MailFooter mail return [string map [list @/n/@ $n] [join $mail \n]] } proc ::m::glue::ComeAround {newcycle} { debug.m/glue {[debug caller] | } # Called when the update cycle comes around back to the start. # Creates a mail reporting on all the projects which where # changed in the previous cycle. set current [m state start-of-current-cycle] m state start-of-previous-cycle $current m state start-of-current-cycle $newcycle m msg "Cycle complete, coming around and starting new ..." set email [m state report-mail-destination] if {$email eq {}} { debug.m/glue {[debug caller] | Skipping report without destination} # Nobody to report to, skipping report m msg "- [color warning {Skipping mail report, no destination}]" return } package require m::mail::generator package require m::mailer m msg "- [color good "Mailing report to"] [color note $email]" set comearound [ComeAroundMail [m state mail-width] $current $newcycle] m mailer to $email [m mail generator reply $comearound {}] m msg [color good OK] return } proc ::m::glue::UpdateRepos {start now repos} { debug.m/glue {[debug caller] | } set n [llength $repos] if {$n} { # The note below is not shown when the user explicitly # specifies the repositories to process. Because that is # outside any cycle. return $repos } set take [m state take] set nrepo [m repo count] set npending [m repo count-pending] m msg "In cycle started on [m format epoch $start]: $take/$npending/$nrepo" # No repositories specified. # Pull repositories directly from pending return [m repo take-pending $take \ ::m::glue::ComeAround $now] } proc ::m::glue::Dedup {values} { debug.m/glue {[debug caller] | } # While keeping the order set res {} |
︙ | ︙ | |||
2326 2327 2328 2329 2330 2331 2332 | } proc ::m::glue::MergeFill {msets} { debug.m/glue {[debug caller] | } set n [llength $msets] if {!$n} { | | | | | | | | | 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 | } proc ::m::glue::MergeFill {msets} { debug.m/glue {[debug caller] | } set n [llength $msets] if {!$n} { # No project. Use the projects for current and previous # repository as merge target and source set target [m rolodex top] if {$target eq {}} { m::cmdr::error \ "No current repository to indicate merge target" \ MISSING CURRENT } set origin [m rolodex next] if {$origin eq {}} { m::cmdr::error \ "No previously current repository to indicate merge source" \ MISSING PREVIOUS } lappend msets [m repo mset $target] [m repo mset $origin] return $msets } if {$n == 1} { # A single project is the merge origin. Use the project of the # current repository as merge target. set target [m rolodex top] if {$target eq {}} { m::cmdr::error \ "No current repository to indicate merge target" \ MISSING CURRENT } return [linsert $msets 0 [m repo mset $target]] } return $msets } proc ::m::glue::Rename {mset newname} { debug.m/glue {[debug caller] | } m project rename $mset $newname # TODO MAYBE: stuff cascading logic into `mset rename` ? foreach store [m project stores $mset] { m store rename $store $newname } return } proc ::m::glue::Merge {target origin} { debug.m/glue {[debug caller] | } # Target and origin are projects # # - Check that all the origin's repositories fit into the target. # This is done by checking the backing stores of the vcs in use # for compatibility. # # - When they do the stores are moved or merged, depending on # - presence of the associated vcs in the target. set vcss [m project used-vcs $origin] # Check that all the origin's repositories fit into the target. foreach vcs $vcss { # Ignore vcs which are not yet used by the target # Assumed to be compatible. if {![m store has $vcs $target]} continue |
︙ | ︙ | |||
2410 2411 2412 2413 2414 2415 2416 | m store move $ostore $target } else { m store merge [m store id $vcs $target] $ostore } } # Move the repositories, drop the origin set, empty after the move | | | | 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 | m store move $ostore $target } else { m store merge [m store id $vcs $target] $ostore } } # Move the repositories, drop the origin set, empty after the move m repo move/project $origin $target m project remove $origin return } proc ::m::glue::MailConfigShow {t {prefix {}}} { debug.m/glue {[debug caller] | } set u [m state mail-user] |
︙ | ︙ | |||
2553 2554 2555 2556 2557 2558 2559 | return $series } ## ## TODO column specific minimum widths ## TODO column specific shaving (currently all on the right, urls: left better, or middle) ## TODO column specific shave commands (ex: size rounding) | | | | 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 | return $series } ## ## TODO column specific minimum widths ## TODO column specific shaving (currently all on the right, urls: left better, or middle) ## TODO column specific shave commands (ex: size rounding) ## TODO ## TODO ## proc ::m::glue::TruncW {titles weights series width} { # series :: list (row) # row :: list (0..n-1 str) # weights :: list (0..k-1 int) # titles :: list (0..n-1 str) - Hard min (for now: include in full width) |
︙ | ︙ | |||
2576 2577 2578 2579 2580 2581 2582 | set n [llength [lindex $series 0]] set k [llength $weights] debug.m/glue { terminal : $width } debug.m/glue { len(series) : [llength $series] } debug.m/glue { len(row) : $n } debug.m/glue { len(weights) : $k ($weights)} | | | | | | | | | > > > > > > | | | | | 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 | set n [llength [lindex $series 0]] set k [llength $weights] debug.m/glue { terminal : $width } debug.m/glue { len(series) : [llength $series] } debug.m/glue { len(row) : $n } debug.m/glue { len(weights) : $k ($weights)} if {$n < $k} { set d [expr {$k - $n}] set weights [lreplace $weights end-$d end] # TODO: Check arith (off by x ?) } if {$n > $k} { set d [expr {$n - $k}] lappend weights {*}[lrepeat $d 0] } # Remove table border overhead to get usable terminal space set width [expr {$width - (3*$n+1)}] debug.m/glue { terminal' : $width (-[expr {3*$n+1}]) } debug.m/glue { weights' : ($weights)} # Compute series column widths (max len) for all columns. If the # total width is larger than width we have to shrink by weight. # Note: Min column width after shrinking is 6 (because we want to # show something for each column). If shrink by weight goes below # this min width bump up to it and remove the needed characters # from the weight 0 columns, but not below min width. set min 6 while {$k} { incr k -1 ; set wc($k) 0 } foreach row [linsert $series 0 $titles] { set col 0 foreach el $row { set n [string length $el] if {$n > $wc($col)} { set wc($col) $n } incr col } } debug.m/glue { col.widths = [W wc] } # max width over all rows. set fw 0 foreach {_ v} [array get wc] { incr fw $v } debug.m/glue { full = $fw vs terminal $width } # Nothing to do if the table fits already if {$fw <= $width} { return [list $titles $series] } # No fit, start shrinking. # Sum of weights to apportion set tw 0 foreach w $weights { if {$w <= 0} continue ; incr tw $w } # Number of characters over the allowed width. set over [expr {$fw - $width}] debug.m/glue { over : $over } # Shrink columns per weight set col 0 ; set removed 0 foreach w $weights { set c $col ; incr col if {$w <= 0} { debug.m/glue { ($col): skip } continue } set drop [format %.0f [expr {double($over * $w)/$tw}]] debug.m/glue { ($col): drop $drop int(($over*$w)/$tw)) } incr removed $drop incr wc($c) -$drop } # --assert: removed >= over debug.m/glue { removed : $removed } # Rounding may cause removed < over, leaving too many characters behind. # Run a constant shaver, on the weighted cols set over [expr {$over - $removed}] if {$over} { ShaveWeighted wc $weights $over } debug.m/glue { col.widths = [W wc] } # If a weighted column has become to small, i.e. less than the # allowed min, in the above we bump it back to that width and will # shave these then from other columns. set col 0 set under 0 foreach w $weights { set c $col ; incr col if {($w <= 0) || ($wc($c) >= $min)} continue incr under [expr {$min - $wc($c)}] set wc($c) $min } debug.m/glue { under : $under } debug.m/glue { col.widths = [W wc] } # Claw back the added characters from other columns now, as much # as we can. We try to shrink other weighted columns first before # goign for the unweighted, i.e. strongly fixed ones. if {$under} { set under [ShaveWeighted wc $weights $under] } if {$under} { set under [ShaveUnweighted wc $weights $under] } debug.m/glue { col.widths = [W wc] } # At last, truncate the series elements to the chosen column # widths. Same for the titles. set new {} foreach row $series { set col 0 set newrow {} foreach el $row { |
︙ | ︙ | |||
2700 2701 2702 2703 2704 2705 2706 | foreach el $titles { if {[string length $el] > $wc($col)} { set el [string range $el 0 $wc($col)-1] } lappend newtitles $el incr col } | | | 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 | foreach el $titles { if {[string length $el] > $wc($col)} { set el [string range $el 0 $wc($col)-1] } lappend newtitles $el incr col } return [list $newtitles $new] } proc ::m::glue::ShaveWeighted {wv weights shave} { set min 6 ;# todo place in common upvar 1 $wv wc set changed 1 |
︙ | ︙ | |||
2756 2757 2758 2759 2760 2761 2762 2763 2764 | return [m format interval [lindex [split [string trim $lastn ,] ,] end]] } proc ::m::glue::StatsTime {mins maxs lastn} { set mins [expr {$mins < 0 ? "+Inf" : [m format interval $mins]}] set maxs [m format interval $maxs] append text "$mins ... $maxs" | > > | | < | < < < | < | | | 2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 | return [m format interval [lindex [split [string trim $lastn ,] ,] end]] } proc ::m::glue::StatsTime {mins maxs lastn} { set mins [expr {$mins < 0 ? "+Inf" : [m format interval $mins]}] set maxs [m format interval $maxs] # See also ::m::repo::times, ::m::web::site::Store append text "$mins ... $maxs" set lastn [m format win $lastn] set n [llength $lastn] if {!$n} { return $text } set lastn [m format win-trim $lastn [m state store-window-size]] set n [llength $lastn] set total [expr [join $lastn +]] set avg [m format interval [format %.0f [expr {double($total)/$n}]]] append text " \[avg $avg (over $n)]" return $text } proc ::m::glue::DeltaSizeFull {current previous} { append text [m format size $current] if {$previous != $current} { if {$current < $previous} { |
︙ | ︙ | |||
2824 2825 2826 2827 2828 2829 2830 | if {$delta < 0} { set color bad } else { # delta > 0 set color note set delta +$delta } | | | | 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 3090 3091 3092 3093 | if {$delta < 0} { set color bad } else { # delta > 0 set color note set delta +$delta } append text $current " (" [color $color "$previous ($delta)"] ")" return $text } proc ::m::glue::DeltaCommit {current previous} { if {$previous == $current} { return $current } set delta [expr {$current - $previous}] if {$delta < 0} { set color bad } else { # delta > 0 set color note set delta +$delta } append text $current " (" [color $color "$delta"] ")" return $text } # # ## ### ##### ######## ############# ###################### return |
Changes to lib/db/db.tcl.
1 2 3 4 5 6 | ## -*- tcl -*- # # ## ### ##### ######## ############# ##################### ## Mirror database - core access and schema # @@ Meta Begin # Package m::db 0 | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | ## -*- tcl -*- # # ## ### ##### ######## ############# ##################### ## Mirror database - core access and schema # @@ Meta Begin # Package m::db 0 # Meta author {Andreas Kupries} # Meta location https://core.tcl.tk/akupries/???? # Meta platform tcl # Meta summary Main database access and schema # Meta description Main database access and schema # Meta subject {database access} schema main # Meta require {Tcl 8.5-} # @@ Meta End package provide m::db 0 # # ## ### ##### ######## ############# ##################### ## Requisites |
︙ | ︙ | |||
100 101 102 103 104 105 106 | I+ C name TEXT NOT NULL UNIQUE T name # - -- --- ----- -------- ------------- ## Mirror Set - Group of repositories holding the same logical set | | | 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 | I+ C name TEXT NOT NULL UNIQUE T name # - -- --- ----- -------- ------------- ## Mirror Set - Group of repositories holding the same logical set ## of files/content. I+ C name INTEGER NOT NULL ^name UNIQUE T mirror_set # - -- --- ----- -------- ------------- ## Repository - A set of versioned files to back up |
︙ | ︙ | |||
128 129 130 131 132 133 134 | C vcs INTEGER NOT NULL ^version_control_system C mset INTEGER NOT NULL ^mirror_set U vcs mset T store # - -- --- ----- -------- ------------- ## Version Control System - Applications able to manage | | | | | | | | | | | | | | 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 | C vcs INTEGER NOT NULL ^version_control_system C mset INTEGER NOT NULL ^mirror_set U vcs mset T store # - -- --- ----- -------- ------------- ## Version Control System - Applications able to manage ## repositories I+ C code TEXT NOT NULL UNIQUE ; # Short semi-internal tag C name TEXT NOT NULL UNIQUE ; # Human readable name T version_control_system >+ 'fossil' 'Fossil' >+ 'git' 'Git' ## State tables # - -- --- ----- -------- ------------- ## Client state - Named values C name TEXT NOT NULL PRIMARY KEY C value TEXT NOT NULL T state > 'limit' '20' ;# Show this many repositories per `list` > 'store' '~/.mirror/store' ;# Directory for the backend stores > 'take' '5' ;# Check this many mirrors sets per `update` > 'top' '' ;# State for `list`, next repository to show. # - -- --- ----- -------- ------------- ## Mirror Set Pending - List of repositories waiting for an update ## to process them C mset INTEGER NOT NULL ^mirror_set PRIMARY KEY T mset_pending # - -- --- ----- -------- ------------- ## Store Times - Per store the times of last update and change # # Notes on the recorded times: # # - Invariant: changed <= updated # Because not every update causes a change. C store INTEGER NOT NULL ^store PRIMARY KEY C updated INTEGER NOT NULL C changed INTEGER NOT NULL T store_times # - -- --- ----- -------- ------------- ## Rolodex - Short hand references to recently seen repositories I C repository INTEGER NOT NULL ^repository UNIQUE T rolodex # - -- --- ----- -------- ------------- return } proc ::m::db::SETUP-201810092200 {} { |
︙ | ︙ | |||
201 202 203 204 205 206 207 | proc ::m::db::SETUP-201810111600 {} { debug.m/db {} # Added column `created` to `store_times` # # Notes on the recorded times: # # - Invariant: changed <= updated | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 | proc ::m::db::SETUP-201810111600 {} { debug.m/db {} # Added column `created` to `store_times` # # Notes on the recorded times: # # - Invariant: changed <= updated # Because not every update causes a change. # # - Invariant: created <= changed # Because a change can happen only after we have store # # - (created == changed) # -> Never seen any change for this store. # # Overall # created <= changed <= updated D m::db # - -- --- ----- -------- ------------- C store INTEGER NOT NULL ^store PRIMARY KEY C created INTEGER NOT NULL C updated INTEGER NOT NULL C changed INTEGER NOT NULL < store_times store updated updated changed # ^ use last update as fake creation return } proc ::m::db::SETUP-201810121600 {} { debug.m/db {} # Added mail configuration to the general state table set h {This is a semi-automated mail by @cmd@, on behalf of @sender@.} D m::db # - -- --- ----- -------- ------------- T^ state # -- Debugging > 'mail-debug' '0' ;# Bool. Activates low-level debugging in smtp/mime # -- SMTP configuration > 'mail-host' 'localhost' ;# Name of the mail-relay host to talk to > 'mail-port' '25' ;# Port where the mail-relay host accepts SMTP > 'mail-user' 'undefined' ;# account accepted by the mail-relay host > 'mail-pass' '' ;# and associated credentials > 'mail-tls' '0' ;# Bool. Activates TLS to secure SMTP transactions # -- Mail content configuration > 'mail-sender' 'undefined' ;# Email address to place into From/Sender headers > 'mail-header' '$h' ;# Text to place before the generated content > 'mail-footer' '' ;# Text to place after the generated content # # Note: Template processing happens after the content # # is assembled, i.e. affects header and footer. return } proc ::m::db::SETUP-201810131603 {} { debug.m/db {} # Add tables for rejection mail content # (submission processing) D m::db # - -- --- ----- -------- ------------- I+ C name TEXT NOT NULL UNIQUE C automail INTEGER NOT NULL C isdefault INTEGER NOT NULL C text TEXT NOT NULL T reply set sm "It is spam" set om "It is off-topic here" set rm "It was intentionally removed before and we will not add it again" >+ 'spam' 0 1 '$sm' ;# default reason >+ 'offtopic' 1 0 '$om' >+ 'removed' 1 0 '$rm' return } proc ::m::db::SETUP-201810141600 {} { debug.m/db {} # Add tables for external submissions # - submissions # - rejected submissions (for easy auto-rejection on replication) D m::db # - -- --- ----- -------- ------------- I+ C url TEXT NOT NULL UNIQUE C email TEXT NOT NULL C submitter TEXT C sdate INTEGER NOT NULL T submission X sdate I+ C url TEXT NOT NULL UNIQUE C reason TEXT NOT NULL T rejected return } proc ::m::db::SETUP-201810311600 {} { debug.m/db {} # Added column `size_kb` for store size to `store`. D m::db # - -- --- ----- -------- ------------- I+ C vcs INTEGER NOT NULL ^version_control_system C mset INTEGER NOT NULL ^mirror_set C size_kb INTEGER NOT NULL U vcs mset < store id vcs mset '0' package require m::store m::store::InitialSizes return } proc ::m::db::SETUP-201811152300 {} { debug.m/db {} # Added site configuration to the general state table D m::db # - -- --- ----- -------- ------------- T^ state # -- Debugging > 'site-active' '0' ;# Site status (active or not) > 'site-store' '~/.mirror/site' ;# Location where website is generated > 'site-mgr-mail' '' ;# Mail address of the site manager > 'site-mgr-name' '' ;# Name of the site manager > 'site-title' 'Mirror' ;# Name of the site > 'site-url' '' ;# The url the site will be published at return } proc ::m::db::SETUP-201811162301 {} { debug.m/db {} # Added more site configuration to the general state table D m::db # - -- --- ----- -------- ------------- T^ state # -- Debugging > 'site-logo' '' ;# Path or url to the site logo. return } proc ::m::db::SETUP-201811202300 {} { debug.m/db {} # Added flag 'active' to repository. # Default: yes. D m::db # - -- --- ----- -------- ------------- I+ C url TEXT NOT NULL UNIQUE C vcs INTEGER NOT NULL ^version_control_system C mset INTEGER NOT NULL ^mirror_set C active INTEGER NOT NULL < repository id url vcs mset '1' X vcs mset return } |
︙ | ︙ | |||
391 392 393 394 395 396 397 | # submissions table. Initialized to empty. Further dropped unique # requirement from url, allowing multiple submissions of the same, # enabling fixing of description, vcode. Added index instead. D m::db # - -- --- ----- -------- ------------- I+ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 | # submissions table. Initialized to empty. Further dropped unique # requirement from url, allowing multiple submissions of the same, # enabling fixing of description, vcode. Added index instead. D m::db # - -- --- ----- -------- ------------- I+ C url TEXT NOT NULL C vcode TEXT C description TEXT C email TEXT NOT NULL C submitter TEXT C sdate INTEGER NOT NULL < submission id url '' '' email submitter sdate X sdate X url return } proc ::m::db::SETUP-201811282200 {} { debug.m/db {} # Added special column `session` to the submissions # table. Initialized to a value the other parts (cli, CGI) will # not generate. Made url + session unique, i.e. primary key. A # session is allowed to overwrite its submissions, but not the # submissions of other sessions. D m::db # - -- --- ----- -------- ------------- I+ C session TEXT NOT NULL C url TEXT NOT NULL C vcode TEXT C description TEXT C email TEXT NOT NULL C submitter TEXT C sdate INTEGER NOT NULL U session url < submission id ':lock:' url vcode description email submitter sdate X sdate X url return } proc ::m::db::SETUP-201812042200 {} { debug.m/db {} # Added sync helper table. # Remember all submissions handled locally (accepted or rejected), # for deletion from the CGI site database on next sync. Note that # we only need the key information, i.e. url + session id. D m::db # - -- --- ----- -------- ------------- C session TEXT NOT NULL C url TEXT NOT NULL U session url T submission_handled return } proc ::m::db::SETUP-201901112300 {} { debug.m/db {} # Added column `attend` to `store_times`. # Column records presence of issues in the # last update for the store. D m::db # - -- --- ----- -------- ------------- C store INTEGER NOT NULL ^store PRIMARY KEY C created INTEGER NOT NULL C updated INTEGER NOT NULL C changed INTEGER NOT NULL C attend INTEGER NOT NULL < store_times store updated updated changed '0' # fake "no issues" during creation ...........^ package require m::store m::store::InitialIssues return } proc ::m::db::SETUP-201901222300 {} { debug.m/db {} # Extended the tables `store` and `store_times` with columns to # track size changes (KB, #revisions) and time statistics for # updates. D m::db # - -- --- ----- -------- ------------- C store INTEGER NOT NULL ^store PRIMARY KEY C created INTEGER NOT NULL C updated INTEGER NOT NULL C changed INTEGER NOT NULL C attend INTEGER NOT NULL C min_seconds INTEGER NOT NULL ;# overall minimum time spent on update C max_seconds INTEGER NOT NULL ;# overall maximum time spent on update C window_seconds STRING NOT NULL ;# time spent on last N updates (list of int) < store_times store updated updated changed attend '-1' '0' '' # Note: A min_seconds value of -1 represents +Infinity. T^ state > 'store-window-size' '10' ;# Window size for `store.window_seconds` I+ C vcs INTEGER NOT NULL ^version_control_system C mset INTEGER NOT NULL ^mirror_set C size_kb INTEGER NOT NULL C size_previous INTEGER NOT NULL C commits_current INTEGER NOT NULL C commits_previous INTEGER NOT NULL U vcs mset < store id vcs mset size_kb size_kb '0' '0' package require m::store m::store::InitialCommits return } proc ::m::db::SETUP-201901242300 {} { debug.m/db {} # New table `store_github_forks`. Adjunct to table `store`, like # `store_times`. Difference: Not general, specific to github # stores. Tracks the number of forks. Primary source is the local # git repository, the information in the table is derived. Used for # easier access to statistics (size x forks ~?~ update time). D m::db # - -- --- ----- -------- ------------- C store INTEGER NOT NULL ^store PRIMARY KEY C nforks INTEGER NOT NULL T store_github_forks package require m::store m::store::InitialForks return } proc ::m::db::SETUP-201901252300 {} { debug.m/db {} # Added more state, start of the current cycle. D m::db # - -- --- ----- -------- ------------- T^ state > 'start-of-current-cycle' '[clock seconds]' ;# As epoch # Fake start for now, self corrects when it comes around. return } proc ::m::db::SETUP-201901252301 {} { debug.m/db {} # And (local) email address for reporting |
︙ | ︙ | |||
565 566 567 568 569 570 571 | D m::db # - -- --- ----- -------- ------------- T^ state > 'start-of-previous-cycle' '[clock seconds]' ;# As epoch # Fake start for now, self corrects when it comes around # next time. | | | 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 | D m::db # - -- --- ----- -------- ------------- T^ state > 'start-of-previous-cycle' '[clock seconds]' ;# As epoch # Fake start for now, self corrects when it comes around # next time. return } proc ::m::db::SETUP-201902052301 {} { debug.m/db {} # Added `svn` to the set of supported VCS. |
︙ | ︙ | |||
588 589 590 591 592 593 594 | proc ::m::db::SETUP-201910031116 {} { debug.m/db {} # Extended mail configuration, width to use for tables. D m::db # - -- --- ----- -------- ------------- T^ state | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 | proc ::m::db::SETUP-201910031116 {} { debug.m/db {} # Extended mail configuration, width to use for tables. D m::db # - -- --- ----- -------- ------------- T^ state # -- Mail content configuration > 'mail-width' '200' ;# Width of tables placed into content return } proc ::m::db::SETUP-201910032120 {} { debug.m/db {} # Drop table `name` as superfluous. The only user of this # information is table `mirror_set`. Fold the data into a # modified `mirror_set`. Further drop the auto-increment. D m::db # - -- --- ----- -------- ------------- I C name TEXT NOT NULL UNIQUE <= mirror_set { SELECT M.id , N.name FROM @@ M , name N WHERE M.name = N.id } / name return } proc ::m::db::SETUP-202207020000 {} { debug.m/db {} D m::db # - -- --- ----- -------- ------------- # Move to schema V3 # - The main change is the explicit representation and handling of # forks. # - A number of small changes renaming and moving various tables # and fields. # - -- --- ----- -------- ------------- # Drop `mset_pending`, and replace with proper `repo_pending`. # ## Repository Pending - List of repositories waiting for an update ## to process them C repository INTEGER NOT NULL ^repository PRIMARY KEY T repo_pending # - -- --- ----- -------- ------------- ## Rename `mirror_set` to `project` as a more suitable name. R "ALTER TABLE mirror_set RENAME TO project" # - -- --- ----- -------- ------------- ## Redo the repositories # ## - Rename `mset` to `project ## - Add store_times.*_seconds ## - Add store reference ## - Add checked stamp ## - Drop # ## ATTENTION -- This is done before updating the store schema ## because the code to find and link the store requires the mset ## reference to be dropped. I+ C url TEXT NOT NULL UNIQUE C project INTEGER NOT NULL ^project C vcs INTEGER NOT NULL ^version_control_system C store INTEGER NOT NULL ^store C fork_origin INTEGER ^repository C is_active INTEGER NOT NULL C has_issues INTEGER NOT NULL C checked INTEGER NOT NULL ;# epoch C min_duration INTEGER NOT NULL ;# overall minimum time spent on update C max_duration INTEGER NOT NULL ;# overall maximum time spent on update C window_duration STRING NOT NULL ;# time spent on last N updates (list of int) < repository id url mset vcs -1 NULL active 0 0 0 0 '' # url proj vcs store fork act issu chk min max win # Store linkage and store_times related information needs code. foreach {repo mset vcs url} [R { SELECT id , project , vcs , url FROM repository }] { # Locate store for repository set store [R [string map [list :mset $mset :vcs $vcs] { SELECT id FROM store WHERE mset = :mset AND vcs = :vcs }]] lassign [R [string map [list :store $store] { SELECT mset, vcs FROM store WHERE id = :store }]] msets vcss #puts stderr "XXX repo = $url/$mset/$vcs => S$store/$msets/$vcss" # Get time information lassign [R [string map [list :store $store] { SELECT min_seconds , max_seconds , window_seconds FROM store_times WHERE store = :store }]] min max win # update repository with store and times R [string map [list :id $repo :min $min :max $max :win $win :store $store] { UPDATE repository SET store = :store , min_duration = :min , max_duration = :max , window_duration = ':win' WHERE id = :id }] } # - -- --- ----- -------- ------------- ## Redo the stores ## - Drop project reference, add various store_times fields. I+ C vcs INTEGER NOT NULL ^version_control_system C size_kb INTEGER NOT NULL C size_previous INTEGER NOT NULL C commits_current INTEGER NOT NULL C commits_previous INTEGER NOT NULL C created INTEGER NOT NULL C updated INTEGER NOT NULL C changed INTEGER NOT NULL <= store { SELECT S.id , S.vcs , S.size_kb , S.size_previous , S.commits_current , S.commits_previous , T.created , T.updated , T.changed FROM @@ S , store_times T WHERE T.store = S.id } # - -- --- ----- -------- ------------- ## Drop various tables which became superfluous due to the ## preceding changes. / mset_pending / store_github_forks / store_times return } # # ## ### ##### ######## ############# ##################### return |
Changes to lib/db/site.tcl.
︙ | ︙ | |||
247 248 249 250 251 252 253 254 255 256 257 258 | C code TEXT NOT NULL UNIQUE ; # Short semi-internal tag C name TEXT NOT NULL UNIQUE ; # Human readable name T vcs # No fixed values here. Copy from main table # `version_control_system` on sync. return } # # ## ### ##### ######## ############# ##################### return | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 | C code TEXT NOT NULL UNIQUE ; # Short semi-internal tag C name TEXT NOT NULL UNIQUE ; # Human readable name T vcs # No fixed values here. Copy from main table # `version_control_system` on sync. return } proc ::m::site::SETUP-202207020000 {} { debug.m/db {} D m::site # - -- --- ----- -------- ------------- # Move to schema V3 # - It is now possible to have multiple stores of the same # kind for a project. Due to forks of a github repository # being explicit, with separate stores. # # => Drop constraint UNIQUE(name, vcode) I+ C name TEXT NOT NULL C vcode TEXT NOT NULL C page TEXT NOT NULL UNIQUE C remotes TEXT NOT NULL C status TEXT NOT NULL -- icon name C size_kb INTEGER NOT NULL C changed INTEGER NOT NULL C updated INTEGER NOT NULL C created INTEGER NOT NULL < store_index \ id name vcode page remotes status size_kb changed updated created X name X remotes return } # # ## ### ##### ######## ############# ##################### return |
Deleted lib/logic/mset.tcl.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Added lib/logic/project.tcl.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 | ## -*- tcl -*- # # ## ### ##### ######## ############# ###################### # @@ Meta Begin # Package m::project 0 # Meta author {Andreas Kupries} # Meta category ? # Meta description ? # Meta location https://core.tcl-lang.org/akupries/m # Meta platform tcl # Meta require ? # Meta subject ? # Meta summary ? # @@ Meta End # # ## ### ##### ######## ############# ###################### package require Tcl 8.5 package require m::db package require m::repo package require m::rolodex package require debug package require debug::caller # # ## ### ##### ######## ############# ###################### namespace eval ::m { namespace export project namespace ensemble create } namespace eval ::m::project { namespace export \ all add remove rename has \ name used-vcs has-vcs size \ stores known spec id count namespace ensemble create } # # ## ### ##### ######## ############# ###################### debug level m/project debug prefix m/project {[debug caller] | } # # ## ### ##### ######## ############# ###################### proc ::m::project::spec {} { debug.m/project {} set lines {} foreach {project pname} [all] { foreach repo [m repo for $project] { set ri [m repo get $repo] dict with ri {} # -> url : repo url # vcs : vcs id # -> vcode : vcs code # project: project id # name : project name # store : id of backing store for repo lappend lines [list R $vcode $url] } lappend lines [list P $pname] } return [join $lines \n] } proc ::m::project::known {} { debug.m/project {} # Return map to project ids. # Keys: # - rolodex ids (+ '@current', '@', '@prev') # - repository urls # - project names set map {} set mid {} # Repository and project information in one trip. m db eval { SELECT P.id AS id , P.name AS name , R.id AS rid , R.url AS url FROM repository R , project P WHERE R.project = P.id } { dict set mid $rid $id dict set map [string tolower $url] $id dict set map [string tolower $name] $id } # See also m::repo::known # Note, different ids! project, not repo. set c {} set p {} set id -1 foreach r [m rolodex get] { set p $c ; set c $r ; incr id dict set map "@$id" [dict get $mid $r] } if {$p ne {}} { set p [dict get $mid $p] dict set map @prev $p dict set map @-1 $p } if {$c ne {}} { set c [dict get $mid $c] dict set map @current $c dict set map @ $c } return $map } proc ::m::project::all {} { debug.m/project {} return [m db eval { SELECT id , name FROM project ORDER BY name ASC }] } proc ::m::project::id {name} { debug.m/project {} return [m db onecolumn { SELECT id FROM project WHERE name = :name }] } proc ::m::project::count {} { debug.m/project {} return [m db onecolumn { SELECT count (*) FROM project }] } proc ::m::project::add {name} { debug.m/project {} m db eval { INSERT INTO project VALUES ( NULL, :name ) } return [m db last_insert_rowid] } proc ::m::project::remove {project} { debug.m/project {} # TODO FILL project/remove -- Verify that the project has no references # anymore, from neither repositories nor stores return [m db eval { DELETE FROM project WHERE id = :project }] } proc ::m::project::rename {project name} { debug.m/project {} m db eval { UPDATE project SET name = :name WHERE id = :project } return } proc ::m::project::has {name} { debug.m/project {} return [m db onecolumn { SELECT count (*) FROM project WHERE name = :name }] } proc ::m::project::stores {project} { debug.m/project {} return [m db eval { SELECT S.id FROM store S , repository R WHERE S.id = R.store AND R.project = :project }] } proc ::m::project::used-vcs {project} { debug.m/project {} return [m db eval { SELECT DISTINCT vcs FROM repository WHERE project = :project }] } proc ::m::project::size {project} { debug.m/project {} return [m db onecolumn { SELECT count (*) FROM repository WHERE project = :project }] } proc ::m::project::has-vcs {project vcs} { debug.m/project {} return [m db onecolumn { SELECT count (*) FROM repository WHERE project = :project AND vcs = :vcs }] } proc ::m::project::name {project} { debug.m/project {} return [m db onecolumn { SELECT name FROM project WHERE id = :project }] } # # ## ### ##### ######## ############# ###################### proc ::m::project::K {x y} { set x } # # ## ### ##### ######## ############# ###################### package provide m::project 0 return |
Changes to lib/logic/repo.tcl.
︙ | ︙ | |||
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | # ( sizep, commits, commitp, mins, maxs, lastn ) # # ## ### ##### ######## ############# ###################### package require Tcl 8.5 package require m::state package require m::rolodex package require debug package require debug::caller # # ## ### ##### ######## ############# ###################### namespace eval ::m { namespace export repo namespace ensemble create } namespace eval ::m::repo { namespace export \ | > | | > > | 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | # ( sizep, commits, commitp, mins, maxs, lastn ) # # ## ### ##### ######## ############# ###################### package require Tcl 8.5 package require m::state package require m::rolodex package require m::format package require debug package require debug::caller # # ## ### ##### ######## ############# ###################### namespace eval ::m { namespace export repo namespace ensemble create } namespace eval ::m::repo { namespace export \ add remove enable move/project move/1 has get name \ store known get-n for forks project search id count \ claim count-pending add-pending drop-pending pending \ take-pending declaim times fork-locations namespace ensemble create } # # ## ### ##### ######## ############# ###################### debug level m/repo debug prefix m/repo {[debug caller] | } |
︙ | ︙ | |||
59 60 61 62 63 64 65 | SELECT id , url FROM repository } { dict set map [string tolower $url] $id } | | | | 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | SELECT id , url FROM repository } { dict set map [string tolower $url] $id } # See also m::project::known # Note, different ids! repository, not project set c {} set p {} set id -1 foreach r [m rolodex get] { set p $c ; set c $r ; incr id dict set map "@$id" $r } |
︙ | ︙ | |||
82 83 84 85 86 87 88 | } return $map } proc ::m::repo::name {repo} { debug.m/repo {} | | | | | | 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | } return $map } proc ::m::repo::name {repo} { debug.m/repo {} # TODO MAYBE - in-memory cache of mapping repo -> name return [m db onecolumn { SELECT R.url || ' (: ' || P.name || ')' FROM repository R , project P WHERE R.id = :repo AND P.id = R.project }] } proc ::m::repo::has {url} { debug.m/repo {} return [m db onecolumn { SELECT count (*) |
︙ | ︙ | |||
118 119 120 121 122 123 124 | debug.m/repo {} return [m db onecolumn { SELECT count (*) FROM repository }] } | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > | | > > > > > > > > > > > < < | | > | | > > > > > | | | | | < | > | | | | | < | < < | | > | 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 | debug.m/repo {} return [m db onecolumn { SELECT count (*) FROM repository }] } proc ::m::repo::times {repo duration now issues} { debug.m/repo {} # Read current state m db eval { SELECT min_duration AS mins , max_duration AS maxs , window_duration AS window FROM repository WHERE id = :repo } {} debug.m/repo {lastr = ($window)} # See also ::m::glue::StatsTime, ::m::web::site::Store set window [m format win $window] debug.m/repo {mins = $mins} debug.m/repo {maxs = $maxs} debug.m/repo {lastn = ($window)} # Modify based on the incoming duration. if {($mins eq {}) || ($mins < 0) || ($duration < $mins)} { set mins $duration } if { $duration > $maxs} { set maxs $duration } lappend window $duration set window [m format win-trim $window [m state store-window-size]] debug.m/repo {last' = ($window)} set window ,[join $window ,], debug.m/repo {last. = ($window)} # And write the results back m db eval { UPDATE repository SET min_duration = :mins , max_duration = :maxs , window_duration = :window , checked = :now , has_issues = :issues WHERE id = :repo } return } proc ::m::repo::add {vcs project store url duration {origin {}}} { debug.m/repo {} set now [clock seconds] if {$origin ne {}} { m db eval { INSERT INTO repository VALUES ( NULL -- id , :url -- url , :project -- project , :vcs -- vcs , :store -- store , :origin -- fork_origin , 1 -- is_active , 0 -- has_issues , :now -- checked , :duration -- min_duration , :duration -- max_duration , :duration -- window_duration ) } } else { m db eval { INSERT INTO repository VALUES ( NULL -- id , :url -- url , :project -- project , :vcs -- vcs , :store -- store , NULL -- fork_origin , 1 -- is_active , 0 -- has_issues , :now -- checked , :duration -- min_duration , :duration -- max_duration , :duration -- window_duration ) } } return [m db last_insert_rowid] } proc ::m::repo::for {project} { debug.m/project {} return [m db eval { SELECT id FROM repository WHERE project = :project }] } proc ::m::repo::forks {repo} { debug.m/project {} return [m db eval { SELECT id FROM repository WHERE fork_origin = :repo }] } proc ::m::repo::fork-locations {repo} { debug.m/project {} return [m db eval { SELECT url FROM repository WHERE fork_origin = :repo }] } proc ::m::repo::project {repo} { debug.m/repo {} set project [m db onecolumn { SELECT project FROM repository WHERE id = :repo }] debug.m/repo {=> ($project)} return $project } proc ::m::repo::store {repo} { debug.m/project {} return [m db eval { SELECT store FROM repository WHERE id = :repo }] } proc ::m::repo::get {repo} { debug.m/repo {} # Given a repository (by id) follow all the links in the database # to retrieve everything related to it # - repository (url) # - project (id, and name) # - vcs (id, and code) # - store (id) # - active set details [m db eval { SELECT 'url' , R.url , 'active' , R.is_active , 'issues' , R.has_issues , 'vcs' , R.vcs , 'vcode' , V.code , 'project', R.project , 'name' , P.name , 'store' , S.id , 'min_sec', min_duration , 'max_sec', max_duration , 'win_sec', window_duration , 'checked', checked , 'origin' , fork_origin FROM repository R , project P , version_control_system V , store S WHERE R.id = :repo AND P.id = R.project AND V.id = R.vcs AND S.id = R.store }] debug.m/repo {=> ($details)} return $details } proc ::m::repo::search {substring} { debug.m/repo {} set sub [string tolower $substring] set series {} m db eval { SELECT P.name AS name , R.fork_origin AS origin , R.url AS url , R.id AS rid , V.code AS vcode , S.size_kb AS sizekb , R.is_active AS active , R.min_duration AS mins , R.max_duration AS maxs , R.window_duration AS lastn , S.size_previous AS sizep , S.commits_current AS commits , S.commits_previous AS commitp FROM repository R , project P , version_control_system V , store S WHERE P.id = R.project AND V.id = R.vcs AND S.id = R.store ORDER BY P.name ASC , R.url ASC } { if { ([string first $sub [string tolower $name]] < 0) && ([string first $sub [string tolower $url ]] < 0) } continue lappend series [dict create \ primary [expr {$origin eq {}}] \ name $name \ url $url \ id $rid \ vcode $vcode \ sizekb $sizekb \ active $active \ sizep $sizep \ |
︙ | ︙ | |||
246 247 248 249 250 251 252 | debug.m/repo {first = ($first)} } lassign $first mname uname set lim [expr {$n + 1}] set replist {} m db eval { | | > | | | | | < | < < | | | | > | 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 | debug.m/repo {first = ($first)} } lassign $first mname uname set lim [expr {$n + 1}] set replist {} m db eval { SELECT P.name AS name , R.fork_origin AS origin , R.url AS url , R.id AS rid , V.code AS vcode , S.size_kb AS sizekb , R.is_active AS active , R.min_duration AS mins , R.max_duration AS maxs , R.window_duration AS lastn , S.size_previous AS sizep , S.commits_current AS commits , S.commits_previous AS commitp FROM repository R , project P , version_control_system V , store S WHERE P.id = R.project AND V.id = R.vcs AND S.id = R.store -- cursor start clause ... AND ((P.name > :mname) OR ((P.name = :mname) AND (R.url >= :uname))) ORDER BY P.name ASC , R.url ASC LIMIT :lim } { lappend replist [dict create \ primary [expr {$origin eq {}}] \ name $name \ url $url \ id $rid \ vcode $vcode \ sizekb $sizekb \ active $active \ sizep $sizep \ |
︙ | ︙ | |||
319 320 321 322 323 324 325 326 327 328 329 330 331 332 | proc ::m::repo::remove {repo} { debug.m/repo {} return [m db eval { DELETE FROM repository WHERE id = :repo }] } proc ::m::repo::enable {repo {flag 1}} { debug.m/repo {} return [m db eval { UPDATE repository | > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > > | > > | | > > > | | > > | > > > > > > > > > > > > | | | > > > | > > > > > > | > | > > > > > > > > > > > > > > > > | > > | | | | | | > > > > | 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 | proc ::m::repo::remove {repo} { debug.m/repo {} return [m db eval { DELETE FROM repository WHERE id = :repo ; -- - - -- --- ----- clear origin links in forks UPDATE repository SET fork_origin = NULL WHERE fork_origin = :repo }] } proc ::m::repo::enable {repo {flag 1}} { debug.m/repo {} return [m db eval { UPDATE repository SET is_active = :flag WHERE id = :repo }] } proc ::m::repo::declaim {repo} { debug.m/repo {} m db eval { UPDATE repository SET fork_origin = NULL WHERE id = :repo } return } proc ::m::repo::claim {origin fork} { debug.m/repo {} m db eval { UPDATE repository SET fork_origin = :origin WHERE id = :fork } return } proc ::m::repo::move/project {projectold projectnew} { debug.m/repo {} m db eval { UPDATE repository SET project = :projectnew WHERE project = :projectold } return } proc ::m::repo::move/1 {repo projectnew} { debug.m/repo {} m db eval { UPDATE repository SET project = :projectnew WHERE id = :repo } return } # # ## ### ##### ######## ############# ###################### ## Management of pending repositories proc ::m::repo::count-pending {} { debug.m/repo {} return [m db onecolumn { SELECT count (*) FROM repo_pending }] } proc ::m::repo::add-pending {repo} { debug.m/repo {} m db eval { INSERT INTO repo_pending VALUES ( :repo ) } return } proc ::m::repo::drop-pending {repo} { debug.m/repo {} return [m db eval { DELETE FROM repo_pending WHERE repository = :repo }] return } proc ::m::repo::pending {} { debug.m/repo {} return [m db eval { SELECT P.name AS name , R.url AS url , R.fork_origin AS origin , (SELECT count (*) FROM repository X WHERE fork_origin = R.id) AS nforks FROM repository R , project P WHERE R.project = P.id AND R.is_active ORDER BY R.ROWID }] } proc ::m::repo::take-pending {take args} { debug.m/repo {} # Ask for one more than actually requested by the # configuration. This will cause a short-read (with refill) not # only when the table contains less than `take` elements, but also # when it contains exactly that many. If the read is not short we # know that at least one element is left. incr take set taken [m db eval { SELECT P.repository FROM repo_pending P , repository R WHERE R.id = P.repository AND R.is_active LIMIT :take }] if {[llength $taken] < $take} { # Short read. Clear taken (fast), and refill for next # invokation. m db eval { DELETE FROM repo_pending ; INSERT INTO repo_pending SELECT id FROM repository } if {[llength $args]} { # Invoke callback to report that the overall cycle just # came around and started anew. try { uplevel 1 $args } on error {e o} { # TODO -- Report (internal) error, but do not crash. } } } else { # Full read. Clear taken, the slow way. Drop the unwanted # sentinel element from the end of the result. set taken [lreplace [K $taken [unset taken]] end end] m db eval [string map [list %% [join $taken ,]] { DELETE FROM repo_pending WHERE repository in (%%) }] } return $taken } # # ## ### ##### ######## ############# ###################### proc ::m::repo::FIRST {} { debug.m/repo {} # First known repository. # Ordered by project name, then url return [m db eval { SELECT P.name , R.url FROM repository R , project P WHERE R.project = P.id ORDER BY P.name ASC , R.url ASC LIMIT 1 }] } # # ## ### ##### ######## ############# ###################### proc ::m::repo::K {x y} { set x } # # ## ### ##### ######## ############# ###################### package provide m::repo 0 return |
Changes to lib/logic/store.tcl.
︙ | ︙ | |||
30 31 32 33 34 35 36 | namespace eval ::m { namespace export store namespace ensemble create } namespace eval ::m::store { namespace export \ | | | > | | | < < | < > | < | | < < | < | < < < < | 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | namespace eval ::m { namespace export store namespace ensemble create } namespace eval ::m::store { namespace export \ add remove move rename merge cleave update has check path \ id vcs-name updates by-name by-size by-vcs move-location \ get getx repos remotes total-size count search issues disabled \ has-issues namespace ensemble create } # # ## ### ##### ######## ############# ###################### debug level m/store debug prefix m/store {[debug caller] | } # # ## ### ##### ######## ############# ###################### proc ::m::store::add {vcs name url} { debug.m/store {} set store [Add $vcs] set state [m vcs setup $store $vcs $name $url] dict with state {} # commits, size, forks, duration Size $store $size Commits $store $commits return [list $store $duration $commits $size $forks] } proc ::m::store::remove {store} { debug.m/store {} set vcs [VCS $store] m db eval { DELETE FROM store WHERE id = :store } m vcs cleanup $store $vcs return |
︙ | ︙ | |||
100 101 102 103 104 105 106 | set new [Add $vcs $msetnew ] set name [MSName $msetnew] m vcs cleave $vcs $store $new $name Size $new return } | | | | < < < | | | | > > | < | < < > | | < < < | < < < < < < | < < < < < | < | | 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 | set new [Add $vcs $msetnew ] set name [MSName $msetnew] m vcs cleave $vcs $store $new $name Size $new return } proc ::m::store::has-issues {store} { return [expr {[lindex [m vcs caps $store] 1] ne {}}] } proc ::m::store::update {primary url store cycle now before} { debug.m/store {} set vcs [VCS $store] set state [m vcs update $store $vcs $url $primary] dict with state {} # ok, commits, size, forks, duration if {!$primary} { set forks {} } debug.m/store {update = ($state)} if {!$ok} { Size $store $size Commits $store $commits Times $store $cycle $now [expr {$commits != $before}] } return [list $ok $duration $commits $size $forks] } proc ::m::store::move {store msetnew} { debug.m/store {} # copy of `m mset name` - outline? check for dependency circles set newname [MSName $msetnew] set vcs [VCS $store] |
︙ | ︙ | |||
194 195 196 197 198 199 200 201 202 | } proc ::m::store::path {store} { debug.m/store {} return [m vcs path $store] } proc ::m::store::get {store} { debug.m/store {} | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < | | | | | | | | < < < | < < < < < | < | > > | > | > | | > > > > > > | 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 | } proc ::m::store::path {store} { debug.m/store {} return [m vcs path $store] } proc ::m::store::getx {repos} { ;# XXX REWORK move to repo package debug.m/store {} lappend map @@ [join $repos ,] set series {} m db eval [string map $map { SELECT S.id AS store , P.name AS mname , V.code AS vcode , S.changed AS changed , S.updated AS updated , S.created AS created , R.has_issues AS attend , S.size_kb AS size , 1 AS remote , R.is_active AS active , R.fork_origin AS origin , R.url AS url , R.id AS rid FROM repository R , store S , project P , version_control_system V WHERE R.store = S.id AND R.project = P.id AND R.vcs = V.id AND R.id IN (@@) ORDER BY mname ASC , vcode ASC , size ASC }] { Srow series ;# upvar column variables } return $series } proc ::m::store::get {store} { debug.m/store {} set details [m db eval { SELECT 'size' , S.size_kb , 'vcs' , S.vcs , 'sizep' , S.size_previous , 'commits' , S.commits_current , 'commitp' , S.commits_previous , 'vcsname' , V.name , 'updated' , S.updated , 'changed' , S.changed , 'created' , S.created , 'attend' , (SELECT sum (R.has_issues) FROM repository R WHERE R.store = S.id) , 'min_sec' , (SELECT min (R.min_duration) FROM repository R WHERE R.store = S.id) , 'max_sec' , (SELECT max (R.max_duration) FROM repository R WHERE R.store = S.id) , 'win_sec' , (SELECT group_concat (R.window_duration) FROM repository R WHERE R.store = S.id) , 'remote' , (SELECT count (*) FROM repository R WHERE R.store = S.id) , 'active' , (SELECT sum (is_active) FROM repository R WHERE R.store = S.id) FROM store S , version_control_system V WHERE S.id = :store AND S.vcs = V.id }] debug.m/store {=> ($details)} return $details } proc ::m::store::remotes {store} { debug.m/store {} return [Remotes $store] } proc ::m::store::repos {store} { debug.m/store {} return [m db eval { SELECT R.id FROM repository R , store S WHERE S.id = :store AND R.store = S.id }] } proc ::m::store::vcs-name {store} { debug.m/store {} return [m db onecolumn { SELECT V.name FROM store S |
︙ | ︙ | |||
265 266 267 268 269 270 271 272 273 274 275 276 277 | proc ::m::store::count {} { debug.m/store {} return [m db onecolumn { SELECT count (*) FROM store }] } proc ::m::store::search {substring} { debug.m/store {} set sub [string tolower $substring] set series {} m db eval { | > > > | | | | | | | | | < | > | | | < < < < | < < < | > > | | | | | | | | | < | > | | | < < < < | | | | | | | | > > | | | | | | | | | | | | | > | < | | < < | | | > > | | | | | | | | | < | | | | < < < < | | | | | > > | | | | | | | | | < | | | | < < < < | | | | | > > | | | | | | | | | < | | | | < < < < | | | | | > > | | | | | | | | | < < < | < < < < | | | | > | > | | | | | | | | | | | | | | < < < | < < < < | | | | > | > | | | | | | | | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > | | > > | 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 | proc ::m::store::count {} { debug.m/store {} return [m db onecolumn { SELECT count (*) FROM store }] } proc ::m::store::search {substring} { debug.m/store {} # List stores ... set sub [string tolower $substring] set series {} m db eval { SELECT S.id AS store , P.name AS mname , V.code AS vcode , S.changed AS changed , S.updated AS updated , S.created AS created , R.has_issues AS attend , S.size_kb AS size , 1 AS remote , R.is_active AS active , R.fork_origin AS origin , R.url AS url , R.id AS rid FROM repository R , store S , project P , version_control_system V ORDER BY mname ASC , vcode ASC , size ASC } { if { [string first $sub [string tolower $mname]] < 0 } continue Srow series ;# upvar column variables } return $series } proc ::m::store::issues {} { ;# XXX REWORK move to repo package debug.m/store {} # List repositories ... set series {} set last {} m db eval { SELECT S.id AS store , P.name AS mname , V.code AS vcode , S.changed AS changed , S.updated AS updated , S.created AS created , R.has_issues AS attend , S.size_kb AS size , 1 AS remote , R.is_active AS active , R.fork_origin AS origin , R.url AS url , R.id AS rid FROM repository R , store S , project P , version_control_system V WHERE R.store = S.id AND R.has_issues = 1 -- Flag for "has issues" AND R.is_active > 0 -- Flag for "not completely disabled" AND R.project = P.id AND R.vcs = V.id ORDER BY mname ASC , vcode ASC , size ASC } { Srow+origin series ;# upvar column variables } return $series } proc ::m::store::disabled {} { ;# XXX REWORK move to repo package debug.m/store {} # List repositories ... set series {} set last {} m db eval { SELECT S.id AS store , P.name AS mname , V.code AS vcode , S.changed AS changed , S.updated AS updated , S.created AS created , R.has_issues AS attend , S.size_kb AS size , 1 AS remote , 0 AS active , R.id AS rid , R.url AS url , R.fork_origin AS origin FROM repository R , store S , project P , version_control_system V WHERE R.store = S.id AND R.is_active = 0 -- Flag for disabled AND R.project = P.id AND R.vcs = V.id ORDER BY mname ASC , vcode ASC , size ASC } { Srow+rid+url series ;# upvar column variables } return $series } proc ::m::store::by-name {} { ;# XXX REWORK move to repo package debug.m/store {} # List stores ... set series {} set last {} m db eval { SELECT S.id AS store , P.name AS mname , V.code AS vcode , S.changed AS changed , S.updated AS updated , S.created AS created , R.has_issues AS attend , S.size_kb AS size , 1 AS remote , R.is_active AS active , R.fork_origin AS origin , R.url AS url FROM repository R , store S , project P , version_control_system V WHERE R.store = S.id AND R.project = P.id AND R.vcs = V.id ORDER BY mname ASC , vcode ASC , size ASC } { if {($last ne {}) && ($last ne $mname)} { Sep series } set saved $mname set mname [expr {($last eq $mname) ? "" : "$mname"}] Srow series ;# upvar column variables set last $saved } return $series } proc ::m::store::by-vcs {} { ;# XXX REWORK move to repo package debug.m/store {} # List repositories ... set series {} m db eval { SELECT S.id AS store , P.name AS mname , V.code AS vcode , S.changed AS changed , S.updated AS updated , S.created AS created , R.has_issues AS attend , S.size_kb AS size , 1 AS remote , R.is_active AS active , R.fork_origin AS origin , R.url AS url FROM repository R , store S , project P , version_control_system V WHERE R.store = S.id AND R.project = P.id AND R.vcs = V.id ORDER BY vcode ASC , mname ASC , size ASC } { Srow series } return $series } proc ::m::store::by-size {} { ;# XXX REWORK move to repo package debug.m/store {} # List repositories ... set series {} m db eval { SELECT S.id AS store , P.name AS mname , V.code AS vcode , S.changed AS changed , S.updated AS updated , S.created AS created , R.has_issues AS attend , S.size_kb AS size , 1 AS remote , R.is_active AS active , R.fork_origin AS origin , R.url AS url FROM repository R , store S , project P , version_control_system V WHERE R.store = S.id AND R.project = P.id AND R.vcs = V.id ORDER BY size DESC , mname ASC , vcode ASC } { Srow series } return $series } proc ::m::store::updates {} { ;# XXX REWORK move to repo package debug.m/store {} # List repositories ... # From the db.tcl notes on store times # # 1. created <= changed <= updated # 2. (created == changed) -> never changed. set series {} # Block 1: Changed stores, changed order descending # Insert separators when `updated` changes. set last {} m db eval { SELECT S.id AS store , P.name AS mname , V.code AS vcode , S.changed AS changed , S.updated AS updated , S.created AS created , R.has_issues AS attend , S.size_kb AS size , 1 AS remote , R.is_active AS active , R.min_duration AS mins , R.max_duration AS maxs , R.window_duration AS lastn , S.size_previous AS sizep , S.commits_current AS commits , S.commits_previous AS commitp , R.fork_origin AS origin , R.url AS url FROM repository R , store S , project P , version_control_system V WHERE R.store = S.id AND R.project = P.id AND R.vcs = V.id AND S.created != S.changed ORDER BY S.changed DESC } { if {($last ne {}) && ($last != $updated)} { Sep series } Srow+delta series set last $updated } debug.m/store {f/[llength $series]} set first [llength $series] # Block 2: All unchanged stores, creation order descending, # i.e. last created top/first. m db eval { SELECT S.id AS store , P.name AS mname , V.code AS vcode , S.changed AS changed , S.updated AS updated , S.created AS created , R.has_issues AS attend , S.size_kb AS size , 1 AS remote , R.is_active AS active , R.min_duration AS mins , R.max_duration AS maxs , R.window_duration AS lastn , S.size_previous AS sizep , S.commits_current AS commits , S.commits_previous AS commitp , R.fork_origin AS origin , R.url AS url FROM repository R , store S , project P , version_control_system V WHERE R.store = S.id AND R.project = P.id AND R.vcs = V.id AND S.created = S.changed ORDER BY S.created DESC } { if {$first} { Sep series } set changed {} set updated {} Srow+delta series set first 0 } debug.m/store {r/[llength $series]} return $series } proc ::m::store::move-location {newpath} { debug.m/store {} m vcs move $newpath return } # # ## ### ##### ######## ############# ###################### proc ::m::store::Srow {sv} { ;# XXX REWORK move to repo package debug.m/store {} upvar 1 \ $sv series store store mname mname vcode vcode \ changed changed updated updated created created \ size size active active remote remote attend attend \ origin origin url url debug.m/store {s=$store, m=$mname, v=$vcode, ch=$changed, up=$updated, cr=$created, sz=$size, r=$remote/$active, trouble=$attend, oring=4origin, url=$url} set row [dict create \ url $url \ origin $origin \ store $store \ mname $mname \ vcode $vcode \ changed $changed \ updated $updated \ created $created \ size $size \ remote $remote \ active $active \ attend $attend \ ] lappend series $row return } proc ::m::store::Srow+origin {sv} { ;# XXX REWORK move to repo package debug.m/store {} upvar 1 \ $sv series store store mname mname vcode vcode \ changed changed updated updated created created \ size size active active remote remote attend attend \ origin origin url url rid rid debug.m/store {s=$store, m=$mname, v=$vcode, ch=$changed, up=$updated, cr=$created, sz=$size, r=$remote/$active, trouble=$attend, origin=$origin, url=$url, rid=$rid} set row [dict create \ rid $rid \ url $url \ store $store \ mname $mname \ vcode $vcode \ changed $changed \ updated $updated \ created $created \ size $size \ remote $remote \ active $active \ attend $attend \ origin $origin ] lappend series $row return } proc ::m::store::Srow+delta {sv} { ;# XXX REWORK move to repo package debug.m/store {} upvar 1 \ $sv series store store mname mname vcode vcode \ changed changed updated updated created created \ size size active active remote remote attend attend \ sizep sizep commits commits commitp commitp mins mins \ maxs maxs lastn lastn origin origin url url debug.m/store {s=$store, m=$mname, v=$vcode, ch=$changed, up=$updated, cr=$created, sz=$size, r=$remote/$active, trouble=$attend} set row [dict create \ url $url \ origin $origin \ store $store \ mname $mname \ vcode $vcode \ changed $changed \ updated $updated \ created $created \ size $size \ |
︙ | ︙ | |||
676 677 678 679 680 681 682 | commits $commits \ commitp $commitp \ ] lappend series $row return } | | | | | > | | | < | | | < > > > > > > > > > > > > > > > > > > > | < | < | < < < < < < < < < < < < | 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 | commits $commits \ commitp $commitp \ ] lappend series $row return } proc ::m::store::Srow+rid+url {sv} { ;# XXX REWORK move to repo package debug.m/store {} upvar 1 \ $sv series store store mname mname vcode vcode \ changed changed updated updated created created \ size size active active remote remote attend attend \ rid rid url url origin origin debug.m/store {s=$store, m=$mname, v=$vcode, ch=$changed, up=$updated, cr=$created, sz=$size, r=$remote/$active, trouble=$attend, rid=$rid, url=$url, origin=$origin} set row [dict create \ store $store \ mname $mname \ vcode $vcode \ changed $changed \ updated $updated \ created $created \ size $size \ remote $remote \ active $active \ attend $attend \ rid $rid \ url $url \ origin $origin ] lappend series $row return } proc ::m::store::Sep {sv} { ;# XXX REWORK move to repo package debug.m/store {} upvar 1 $sv series lappend series { store . mname . vcode . changed . updated . created . size . active . remote . attend . rid . url . mins . maxs . lastn . sizep . commits . commitp . } return } proc ::m::store::Remotes {store {onlyactive 0}} { debug.m/store {} if {$onlyactive} { return [m db eval { SELECT R.url FROM repository R , store S WHERE S.id = :store AND R.store = S.id AND R.is_active }] } return [m db eval { SELECT R.url FROM repository R , store S WHERE S.id = :store AND R.store = S.id }] } proc ::m::store::Times {store cycle now haschanged} { if {$haschanged} { m db eval { UPDATE store SET updated = :cycle , changed = :now WHERE store = :store } return } m db eval { UPDATE store SET updated = :cycle WHERE store = :store } return } proc ::m::store::Size {store new} { debug.m/store {} set current [m db onecolumn { SELECT size_kb FROM store WHERE id = :store }] if {$new == $current} return m db eval { UPDATE store SET size_previous = size_kb -- Parallel assignment , size_kb = :new -- Shift values. WHERE id = :store } return } proc ::m::store::Commits {store new} { debug.m/store {} set current [m db onecolumn { |
︙ | ︙ | |||
795 796 797 798 799 800 801 | SET commits_previous = commits_current -- Parallel assignment , commits_current = :new -- Shift values. WHERE id = :store } return } | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | < < < < < | < < < < < < > > > | < < < < < < < < < < < < < < < < | | | > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 | SET commits_previous = commits_current -- Parallel assignment , commits_current = :new -- Shift values. WHERE id = :store } return } proc ::m::store::Add {vcs} { debug.m/store {} set now [clock seconds] m db eval { INSERT INTO store VALUES ( NULL -- id , :vcs -- vcs , 0 -- size_kb , 0 -- size_previous , 0 -- commits_current , 0 -- commits_previous , :now -- created , :now -- updated , :now -- changed ) } return [m db last_insert_rowid] } proc ::m::store::VCS {store} { debug.m/store {} return [m db onecolumn { SELECT vcs FROM store WHERE id = :store }] } proc ::m::store::MSName {project} { debug.m/store {} return [m db onecolumn { SELECT name FROM project WHERE id = :project }] } ## # # ## ### ##### ######## ############# ###################### ## ATTENTION ## These commands are part of the database migration step. ## Their use of old tables and columns is intentional! ## At the point they are called by the migration these are ## the current tables and columns proc ::m::store::InitialCommits {} { debug.m/store {} m db eval { SELECT id FROM store } { InitialCommit $id } return } proc ::m::store::InitialCommit {store} { debug.m/store {} set vcs [VCS $store] set revs [m vcs revs $store $vcs] m db eval { UPDATE store SET commits_current = :revs , commits_previous = :revs WHERE id = :store } return } proc ::m::store::InitialSizes {} { debug.m/store {} m db eval { SELECT id FROM store } { InitialSize $id } return } proc ::m::store::InitialSize {store} { debug.m/store {} set new [m vcs size $store] set current [m db onecolumn { SELECT size_kb FROM store WHERE id = :store }] if {$new == $current} return m db eval { UPDATE store SET size_previous = size_kb -- Parallel assignment , size_kb = :new -- Shift values. WHERE id = :store } return } proc ::m::store::InitialIssues {} { debug.m/store {} m db eval { SELECT id FROM store } { Attend $id } return } proc ::m::store::Attend {store} { debug.m/store {} set attend [expr {[lindex [m vcs caps $store] 1] ne {}}] m db eval { UPDATE store_times SET attend = :attend WHERE store = :store } return } proc ::m::store::InitialForks {} { debug.m/store {} m db eval { SELECT S.id AS store FROM store S , version_control_system V |
︙ | ︙ | |||
963 964 965 966 967 968 969 | set forks [llength [lindex [m vcs github remotes [m vcs path $store]] 1]] m db eval { INSERT INTO store_github_forks VALUES ( :store , :forks ) } | < < < < < < < < < < < < < < < < < < < < < < < | 972 973 974 975 976 977 978 979 980 981 982 983 984 | set forks [llength [lindex [m vcs github remotes [m vcs path $store]] 1]] m db eval { INSERT INTO store_github_forks VALUES ( :store , :forks ) } return } # # ## ### ##### ######## ############# ###################### package provide m::store 0 return |
Deleted lib/logic/vt_mirrorset.tcl.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Added lib/logic/vt_project.tcl.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | ## -*- tcl -*- # # ## ### ##### ######## ############# ##################### ## Repositories - Validation # @@ Meta Begin # Package m::validate::project 0 # Meta author {Andreas Kupries} # Meta location https://core.tcl.tk/akupries/???? # Meta platform tcl # Meta summary mirror set validation # Meta description mirror set validation # Meta subject {mirror set - validation} # Meta require {Tcl 8.5-} # @@ Meta End package provide m::validate::project 0 # # ## ### ##### ######## ############# ##################### ## Requisites package require Tcl 8.5 package require cmdr::validate::common 1.2 package require try package require m::project package require m::repo package require m::rolodex package require m::match package require debug package require debug::caller # # ## ### ##### ######## ############# ###################### debug level m/validate/project debug prefix m/validate/project {[debug caller] | } # # ## ### ##### ######## ############# ##################### ## Definition namespace eval ::m { namespace export validate namespace ensemble create } namespace eval ::m::validate { namespace export project namespace ensemble create } namespace eval ::m::validate::project { namespace export default validate complete release namespace ensemble create namespace import ::cmdr::validate::common::fail namespace import ::cmdr::validate::common::complete-enum } # # ## ### ##### ######## ############# ##################### debug define m/validate/project debug level m/validate/project debug prefix m/validate/project {[debug caller] | } # # ## ### ##### ######## ############# ##################### proc ::m::validate::project::release {p x} { return } proc ::m::validate::project::default {p} { return [m repo mset [m rolodex top]] } proc ::m::validate::project::complete {p x} { debug.m/validate/project {} 10 return [complete-enum [dict keys [m project known]] 0 $x] } proc ::m::validate::project::validate {p x} { debug.m/validate/project {} set known [m project known] set match [m match substring id $known nocase $x] switch -exact -- $match { ok { return $id } fail { fail $p PROJECT "a project" $x } ambiguous { fail $p PROJECT "an unambiguous project" $x } } } # # ## ### ##### ######## ############# ##################### ## Ready return |
Changes to lib/mail/sender.tcl.
︙ | ︙ | |||
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | set token [mime::initialize -string $corpus] m msg* " To: [color name $receiver] ... " try { set res [smtp::sendmessage $token -header [list To $receiver] {*}[Config]] foreach item $res { m msg " ERR $item" } } finally { } mime::finalize $token m msg [color good OK] return } | > > > | 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | set token [mime::initialize -string $corpus] m msg* " To: [color name $receiver] ... " try { set res [smtp::sendmessage $token -header [list To $receiver] {*}[Config]] # XXX REVISIT This may exit on issues, instead of throwing an error ?! foreach item $res { m msg " ERR $item" } } on error {e o} { m msg [color bad $e] } finally { } mime::finalize $token m msg [color good OK] return } |
︙ | ︙ |
Changes to lib/utils/format.tcl.
︙ | ︙ | |||
27 28 29 30 31 32 33 | debug level m/format debug prefix m/format {[debug caller] | } # # ## ### ##### ######## ############# ##################### ## Definition namespace eval m::format { | | > > > > > > > > > > > > > > > > > > > > | 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | debug level m/format debug prefix m/format {[debug caller] | } # # ## ### ##### ######## ############# ##################### ## Definition namespace eval m::format { namespace export size epoch epoch/short interval win win-trim namespace ensemble create } namespace eval m { namespace export format namespace ensemble create } # # ## ### ##### ######## ############# ###################### proc m::format::win {lastn} { # CSV to list, remove bubbles (empty elements) return [lmap x [split $lastn ,] { if {$x eq {}} continue ; set x }] } proc m::format::win-trim {lastn max} { set len [llength $lastn] # As new entries are added at the end trimming is done from the front. # This is a naive trimmer, removing elements one by one. # Considered ok because we usually need only remove one element anyway. while {$len > $max} { set lastn [lrange $lastn 1 end] set len [llength $lastn] } return $lastn } # # ## ### ##### ######## ############# ###################### proc m::format::size {x} { # x is in [KB]. debug.m/format {} if {$x < 1024} { return ${x}K } set x [expr {$x/1024.}] ; if {$x < 1024} { return [format %.1f $x]M } set x [expr {$x/1024.}] ; if {$x < 1024} { return [format %.1f $x]G } set x [expr {$x/1024.}] ; if {$x < 1024} { return [format %.1f $x]T } set x [expr {$x/1024.}] ; if {$x < 1024} { return [format %.1f $x]P } set x [expr {$x/1024.}] ; return [format %.1f $x]E |
︙ | ︙ |
Changes to lib/utils/ops.tcl.
︙ | ︙ | |||
38 39 40 41 42 43 44 | } namespace eval m::ops { namespace export set client namespace ensemble create } namespace eval m::ops::client { namespace export set main \ | | | 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | } namespace eval m::ops { namespace export set client namespace ensemble create } namespace eval m::ops::client { namespace export set main \ info note warn err fatal \ result ok fail commits fork size \ ok? namespace ensemble create } # # ## ### ##### ######## ############# ###################### ## API |
︙ | ︙ | |||
96 97 98 99 100 101 102 103 104 105 106 107 108 109 | proc ::m::ops::client::Cmdline {v} { debug.m/ops/client {} global argv if {[llength $argv] < 3} { Usage "Not enough arguments" } set argv [lassign $argv vcs logfile operation] set ops { setup {Store Url} cleanup {Store} update {Store Url Bool} mergable? {Store Store} merge {Store Store} split {Store Store} | > > > > > > > > > > > > > | 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 | proc ::m::ops::client::Cmdline {v} { debug.m/ops/client {} global argv if {[llength $argv] < 3} { Usage "Not enough arguments" } set argv [lassign $argv vcs logfile operation] # All issues, including syntax errors, bad arguments, etc are # reported through the log and stdout. This is in an internal # support application the user normally will not invoke directly. # Thus the log has to be initialized before anything other checks. if {[catch { LogTo $logfile } msg]} { err $msg fail return 0 } set ops { setup {Store Url} cleanup {Store} update {Store Url Bool} mergable? {Store Store} merge {Store Store} split {Store Store} |
︙ | ︙ | |||
117 118 119 120 121 122 123 | set types [dict get $ops $operation] if {[llength $argv] != [llength $types]} { Usage "Wrong # Args for $operation" } foreach a $argv t $types { if {![$t $a]} { Usage "Expected $t, got '$a'" } } | < < < < < < < | 130 131 132 133 134 135 136 137 138 139 140 141 142 143 | set types [dict get $ops $operation] if {[llength $argv] != [llength $types]} { Usage "Wrong # Args for $operation" } foreach a $argv t $types { if {![$t $a]} { Usage "Expected $t, got '$a'" } } upvar 1 $v cmd set cmd [linsert $argv 0 $vcs $operation] return 1 } proc ::m::ops::client::Usage {{note {}}} { debug.m/ops/client {} |
︙ | ︙ |
Changes to lib/utils/setup.tcl.
1 2 3 4 5 6 | ## -*- tcl -*- # # ## ### ##### ######## ############# ##################### ## Database utilities - Setup, migration processing, schema management # @@ Meta Begin # Package db::setup 0 | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | ## -*- tcl -*- # # ## ### ##### ######## ############# ##################### ## Database utilities - Setup, migration processing, schema management # @@ Meta Begin # Package db::setup 0 # Meta author {Andreas Kupries} # Meta location https://core.tcl.tk/akupries/???? # Meta platform tcl # Meta summary Database setup, migration management # Meta description Database setup, migration management # Meta subject {database setup} {migration processing} {schema management} # Meta require {Tcl 8.5-} # @@ Meta End package provide db::setup 0 package require debug |
︙ | ︙ | |||
29 30 31 32 33 34 35 | debug prefix db/setup {[debug caller] | } # # ## ### ##### ######## ############# ##################### ## Definition namespace eval db::setup { namespace import ::db::track::it ; rename it track | | | 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | debug prefix db/setup {[debug caller] | } # # ## ### ##### ######## ############# ##################### ## Definition namespace eval db::setup { namespace import ::db::track::it ; rename it track namespace export D C U T T^ I I+ > >+ X < <= / R } namespace eval db { namespace export setup namespace ensemble create } |
︙ | ︙ | |||
162 163 164 165 166 167 168 | return } proc db::setup::<= {table select} { debug.db/setup {} T new_${table} | | | | 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 | return } proc db::setup::<= {table select} { debug.db/setup {} T new_${table} # constraint: to not lose rows in the change we count before, then count again after set old [lindex [R "SELECT count (*) FROM $table"] 0] lappend map @@ $table set select [string map $map $select] lappend sql "INSERT INTO new_${table} $select" lappend sql "DROP TABLE $table" lappend sql "ALTER TABLE new_${table} RENAME TO $table" R [join $sql ";\n"] |
︙ | ︙ | |||
236 237 238 239 240 241 242 | return } proc db::setup::InitializeAndGetVersion {db} { debug.db/setup {} return [$db eval [string map [list \t {}] { CREATE TABLE IF NOT EXISTS schema | | | 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 | return } proc db::setup::InitializeAndGetVersion {db} { debug.db/setup {} return [$db eval [string map [list \t {}] { CREATE TABLE IF NOT EXISTS schema ( key TEXT NOT NULL PRIMARY KEY , version INTEGER NOT NULL ) ; INSERT OR IGNORE INTO schema VALUES ('version', 0) ; |
︙ | ︙ |
Changes to lib/vcs/github.tcl.
︙ | ︙ | |||
217 218 219 220 221 222 223 | } # # ## ### ##### ######## ############# ##################### ## Helpers proc ::m::vcs::github::ReportForks {url} { debug.m/vcs/github {} | > | | | 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 | } # # ## ### ##### ######## ############# ##################### ## Helpers proc ::m::vcs::github::ReportForks {url} { debug.m/vcs/github {} upvar 1 path path # for `git::Get` - TODO - redesign with proper state in the low-level code. set origin [join [lrange [file split $url] end-1 end] /] set forks [lsort -dict [m::vcs::git::Get hub forks --raw $origin]] if {[m exec err-last-get]} { m ops client fail ; return } foreach fork $forks { # unverified estimate (saved) m ops client fork https://github.com/$fork } return } # # ## ### ##### ######## ############# ##################### return |
Changes to lib/vcs/vcs.tcl.
︙ | ︙ | |||
52 53 54 55 56 57 58 | } namespace eval ::m::vcs { namespace export \ setup cleanup update check cleave merge \ rename id supported all code name \ detect url-norm name-from-url version \ | | | 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | } namespace eval ::m::vcs { namespace export \ setup cleanup update check cleave merge \ rename id supported all code name \ detect url-norm name-from-url version \ move size caps export path revs namespace ensemble create namespace import ::cmdr::color # Operation state: Id counter, and state per operation. variable opsid 0 variable ops {} |
︙ | ︙ | |||
100 101 102 103 104 105 106 | # return [$vcode revs $path] # } proc ::m::vcs::setup {store vcs name url} { debug.m/vcs {} # store id -> Using for path. # vcs id -> Decode to plugin name | | | | 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | # return [$vcode revs $path] # } proc ::m::vcs::setup {store vcs name url} { debug.m/vcs {} # store id -> Using for path. # vcs id -> Decode to plugin name # name - project name # url - repo url set path [Path $store] set vcode [code $vcs] # Ensure clean new environment file delete -force -- $path file mkdir $path m futil write $path/%name $name ;# Project m futil write $path/%vcs $vcode ;# Manager # Redirect through an external command. This command is currently # always `mirror-vcs VCS LOG setup STORE URL`. # Ask plugin to fill the store. |
︙ | ︙ | |||
135 136 137 138 139 140 141 | # [x] duration if {!$ok} { # Roll back filesystem changes file delete -force -- $path # Rethrow as something more distinguished for trapping | | > > | | | | | | < < | | > < < < | | | | > > < < < < < < < < < < < < < < < < < < < < < < < | 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 | # [x] duration if {!$ok} { # Roll back filesystem changes file delete -force -- $path # Rethrow as something more distinguished for trapping E $msg CHILD } dict unset state results dict unset state msg dict unset state ok # commits, size, forks, duration return $state } proc ::m::vcs::update {store vcs url primary} { debug.m/vcs {} # store id -> Using for path. # vcs id -> Decode to plugin name # urls - repo urls to use as sources set path [Path $store] set vcode [code $vcs] # Validate the url to ensure that it is still present. No need to # go for the vcs client when we know that it must fail. That said, # we store our failure as a pseudo error log for other parts to # pick up on. m futil write $path/%stderr "" m futil write $path/%stdout "Verifying url ...\n" debug.m/vcs {Verifying $url ...} set ok [m url ok $url xr] if {!$ok} { m futil append $path/%stderr " Bad url: $u\n" m futil append $path/%stderr "Unable to reach remote\n" # Fake an error state ... return {ok 0 commits 0 size 0 forks {} results {} msg {Invalid url} duration 0} } # Ask plugin to update the store. # Redirect through an external command. This command is currently # always `mirror-vcs VCS LOG setup STORE URL`. Operation ::m::vcs::OpComplete $vcode update \ {*}[OpCmd $vcode $path $url $primary] set state [OpWait] return $state dict with state {} # [x] ok # [x] commits # [x] size # [x] forks # [ ] results # [x] msg # [x] duration } proc ::m::vcs::rename {store name} { debug.m/vcs {} # store id -> Using for path. # name - new mset name set path [Path $store] |
︙ | ︙ | |||
251 252 253 254 255 256 257 | # [ ] results # [x] msg # [ ] duration if {!$ok} { # Do not perform any filesystem changes. # Rethrow as something more distinguished for trapping | | | 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 | # [ ] results # [x] msg # [ ] duration if {!$ok} { # Do not perform any filesystem changes. # Rethrow as something more distinguished for trapping E $msg CHILD } # ... and the store directory file delete -force -- $path return } |
︙ | ︙ | |||
296 297 298 299 300 301 302 | proc ::m::vcs::check {vcs storea storeb} { debug.m/vcs {} set patha [Path $storea] set pathb [Path $storeb] set vcode [code $vcs] | < < < | < < < | | 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 | proc ::m::vcs::check {vcs storea storeb} { debug.m/vcs {} set patha [Path $storea] set pathb [Path $storeb] set vcode [code $vcs] # Redirect through an external command. This command is currently # always `mirror-vcs VCS LOG mergable?`. Operation ::m::vcs::OpComplete $vcode mergable? \ {*}[OpCmd $vcode $patha $pathb] set state [OpWait] dict with state {} # [x] ok # [ ] commits # [ ] size # [ ] forks # [x] results # [x] msg # [ ] duration if {!$ok} { if {[llength $msg]} { lappend issues {*}$msg } if {[llength $results]} { lappend issues {*}$results } E [join $issues \n] CHILD } else { set flag [lindex $results 0] debug.m/vcs {--> $flag} return $flag } } proc ::m::vcs::merge {vcs target origin} { debug.m/vcs {} set ptarget [Path $target] set porigin [Path $origin] set vcode [code $vcs] # Redirect through an external command. This command is currently # always `mirror-vcs VCS LOG merge`. Operation ::m::vcs::OpComplete $vcode merge \ {*}[OpCmd $vcode $ptarget $porigin] set state [OpWait] dict with state {} # [x] ok # [ ] commits # [ ] size # [ ] forks # [ ] results # [x] msg # [ ] duration if {!$ok} { if {[llength $msg]} { lappend issues {*}$msg } if {[llength $results]} { lappend issues {*}$results } E [join $issues \n] CHILD } # Destroy the merged store cleanup $origin $vcs return } |
︙ | ︙ | |||
377 378 379 380 381 382 383 | file copy -force -- $porigin $pdst # Inlined rename of origin's new copy m futil write $pdst/%name $dstname # Split/create vcs specific special resources, if any ... | < < < > < < < < < < < < < | | | > | 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 | file copy -force -- $porigin $pdst # Inlined rename of origin's new copy m futil write $pdst/%name $dstname # Split/create vcs specific special resources, if any ... # Redirect through an external command. This command is currently # always `mirror-vcs VCS LOG split`. Operation ::m::vcs::OpComplete $vcode split \ {*}[OpCmd $vcode $porigin $pdst] set state [OpWait] dict with state {} # [x] ok # [ ] commits # [ ] size # [ ] forks # [ ] results # [x] msg # [ ] duration if {!$ok} { if {[llength $msg]} { lappend issues {*}$msg } if {[llength $results]} { lappend issues {*}$results } E [join $issues \n] CHILD } return } proc ::m::vcs::path {store} { debug.m/vcs {} return [Path $store] } proc ::m::vcs::export {vcs store} { debug.m/vcs {} set path [Path $store] set vcode [code $vcs] # Redirect through an external command. This command is currently # always `mirror-vcs VCS LOG export STORE`. # Ask plugin for CGI script to access the store. Operation ::m::vcs::OpComplete $vcode export \ {*}[OpCmd $vcode $path] set state [OpWait] dict with state {} # [x] ok # [ ] commits # [ ] size # [ ] forks # [x] results # [ ] msg # [ ] duration if {!$ok} { if {![llength $results]} { lappend results "Failed to retrieve export script for $vcode on $path" } E [join $results \n] EXPORT } else { set script [join $results \n] debug.m/vcs {--> $script} return $script } } # # ## ### ##### ######## ############# ##################### proc ::m::vcs::version {vcode iv} { debug.m/vcs {} upvar 1 $iv issues set issues {} # Redirect through an external command. This command is currently # always `mirror-vcs VCS LOG version`. Operation ::m::vcs::OpComplete $vcode version \ |
︙ | ︙ | |||
499 500 501 502 503 504 505 | github detect $url git detect $url hg detect $url svn detect $url fossil detect $url | | | 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 | github detect $url git detect $url hg detect $url svn detect $url fossil detect $url E "Unable to determine vcs for $url" DETECT } proc ::m::vcs::url-norm {vcode url} { debug.m/vcs {} # Normalize the incoming url # I.e. for a number of known sites, force the use of the https # they support. Further strip known irrelevant trailers. |
︙ | ︙ | |||
531 532 533 534 535 536 537 | } return [string map $map $url] } proc ::m::vcs::name-from-url {vcode url} { debug.m/vcs {} | < < | | 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 | } return [string map $map $url] } proc ::m::vcs::name-from-url {vcode url} { debug.m/vcs {} # Redirect through an external command. This command is currently # always `mirror-vcs VCS LOG url-to-name`. Operation ::m::vcs::OpComplete $vcode url-to-name \ {*}[OpCmd $vcode $url] set state [OpWait] dict with state {} # [x] ok # [ ] commits # [ ] size # [ ] forks # [x] results # [x] msg # [ ] duration if {!$ok} { if {[llength $msg]} { lappend issues {*}$msg } if {[llength $results]} { lappend issues {*}$results } E [join $issues \n] CHILD } else { set name [lindex $results 0] debug.m/vcs {--> $name} return $name } } |
︙ | ︙ | |||
621 622 623 624 625 626 627 | SELECT id FROM version_control_system WHERE name = :x }] } if {$id eq {}} { | | | 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 | SELECT id FROM version_control_system WHERE name = :x }] } if {$id eq {}} { E "Invalid vcs code or name" INTERNAL } return $id } # # ## ### ##### ######## ############# ##################### |
︙ | ︙ | |||
655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 | proc ::m::vcs::Path {dir} { debug.m/vcs {} set path [file normalize [file join [m state store] $dir]] debug.m/vcs {=> $path} return $path } # # ## ### ##### ######## ############# ##################### ## Background operations. Based on jobs. # ## Caller side # - Operation DONE VCS OP ... # - OpCmd VCS ... # proc ::m::vcs::OpComplete {state} { debug.m/vcs {} variable opsresult $state return } proc ::m::vcs::OpWait {} { debug.m/vcs {} vwait ::m::vcs::opsresult return $::m::vcs::opsresult } proc ::m::vcs::OpCmd {vcs args} { debug.m/vcs {} # Currently only fallback for builtin systems. # TODO: Query system configuration first. | > > > > > > > | 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 | proc ::m::vcs::Path {dir} { debug.m/vcs {} set path [file normalize [file join [m state store] $dir]] debug.m/vcs {=> $path} return $path } proc ::m::vcs::E {msg args} { return -code error -errorcode [linsert $args 0 MIRROR VCS] $msg } # # ## ### ##### ######## ############# ##################### ## Background operations. Based on jobs. # ## Caller side # - Operation DONE VCS OP ... # - OpCmd VCS ... # proc ::m::vcs::OpComplete {state} { debug.m/vcs {} variable opsresult $state return } proc ::m::vcs::OpWait {} { debug.m/vcs {} vwait ::m::vcs::opsresult #array set __ $::m::vcs::opsresult ; parray __ return $::m::vcs::opsresult } proc ::m::vcs::OpCmd {vcs args} { debug.m/vcs {} # Currently only fallback for builtin systems. # TODO: Query system configuration first. |
︙ | ︙ |
Changes to lib/web/site.tcl.
︙ | ︙ | |||
22 23 24 25 26 27 28 | package require debug package require debug::caller package require m::asset package require m::db package require m::exec package require m::format package require m::futil | | | 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | package require debug package require debug::caller package require m::asset package require m::db package require m::exec package require m::format package require m::futil package require m::project package require m::site package require m::state package require m::store package require m::vcs # # ## ### ##### ######## ############# ###################### |
︙ | ︙ | |||
74 75 76 77 78 79 80 81 82 83 84 85 86 87 | ! "= Data dependent content ..." Contact Export ;# (See `export`) Search Submit Stores set bytime [m store updates] set byname [m store by-name] set bysize [m store by-size] set byvcs [m store by-vcs] set issues [m store issues] ;# excludes disabled set disabled [m store disabled] | > > > > > > > > > > > | | | | | | | | | 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | ! "= Data dependent content ..." Contact Export ;# (See `export`) Search Submit Stores # Statistics page # - cycle information (start, end, duration, last duration) # - number of projects, repos, stores # - min, max, average, median n.commits, size.kb # Project list - # repos, link to details # Project details - repo list, store links! # constrained lists -- # -- just primaries, no forks # -- per VCS, just managed by such set bytime [m store updates] set byname [m store by-name] set bysize [m store by-size] set byvcs [m store by-vcs] set issues [m store issues] ;# excludes disabled set disabled [m store disabled] dict set stats issues [llength $issues] dict set stats disabled [llength $disabled] dict set stats size [m store total-size] dict set stats nrepos [m repo count] dict set stats nprojects [m project count] dict set stats nstores [m store count] dict set stats ccycle [m state start-of-current-cycle] dict set stats pcycle [m state start-of-previous-cycle] List "By Last Change" index.md $bytime $stats List "By Name, VCS, Size" index_name.md $byname $stats List "By Size, Name, VCS" index_size.md $bysize $stats List "By VCS, Name, Size" index_vcs.md $byvcs $stats List "Issues by Name" index_issues.md $issues $stats List "Disabled by Name" index_disabled.md $disabled $stats |
︙ | ︙ | |||
118 119 120 121 122 123 124 | uplevel 1 $script unset dst silent return } proc ::m::web::site::Stores {} { debug.m/web/site {} | | > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | < < < < | < < < < < < < < < < < < < < < < < < < < < < < < | | < < < < < < < < < < < | | < < < < | < | | > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > < < < < < < < < < < < < < < < < < < < < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | | | | | | | > > > > > | | | | > | | > > > > > > > > > > | | 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 | uplevel 1 $script unset dst silent return } proc ::m::web::site::Stores {} { debug.m/web/site {} foreach {project name} [m project all] { foreach store [m project stores $project] { Store $project $name $store } } return } proc ::m::web::site::RLink {repo {follow 1}} { debug.m/web/site {} set ri [m repo get $repo] dict with ri {} # active, issues, url, store set active [expr {$active ? "" : "[I images/off.svg "Offline"]"}] set issues [expr {!$issues ? "" : "[I images/bad.svg "Attend"]"}] if {!$follow} { set origin {} } if {$origin ne {}} { set origin " a [ForkLogo] from [OLink $origin]" } set label $active$issues$url return [LB $url $label]$origin } proc ::m::web::site::OLink {repo} { debug.m/web/site {} set ri [m repo get $repo] dict with ri {} # active, issues, url, store set active [expr {$active ? "" : "[I images/off.svg "Offline"]"}] set issues [expr {!$issues ? "" : "[I images/bad.svg "Attend"]"}] set label $active$issues$url return [LB store_${store}.html $label] } proc ::m::web::site::StatsTime {min_sec max_sec win_sec} { debug.m/web/site {} # See also ::m::repo::times, ::m::glue::StatsTime set min_sec [expr {$min_sec < 0 ? "+Inf" : [m format interval $min_sec]}] set max_sec [m format interval $max_sec] set spent "$min_sec ... $max_sec" set win_sec [m format win $win_sec] set n [llength $win_sec] if {$n} { set win_sec [m format win-trim $win_sec [m state store-window-size]] set total [expr [join $win_sec +]] set avg [m format interval [format %.0f [expr {double($total)/$n}]]] append spent " \[avg $avg (over $n)]" } return $spent } proc ::m::web::site::Commits {commits commitp} { debug.m/web/site {} if {$commitp != $commits} { set delta [expr {$commits - $commitp}] if {$delta > 0} { set delta +$delta } append commits " ($commitp ($delta))" } return $commits } proc ::m::web::site::Size {size sizep} { debug.m/web/site {} set dsize [m format size $size] if {$sizep != $size} { set dsizep [m format size $sizep] if {$size < $sizep} { # shrink set delta -[m format size [expr {$sizep - $size}]] } else { # grow set delta +[m format size [expr {$size - $sizep}]] } append dsize " ($dsizep ($delta))" } return $dsize } proc ::m::web::site::ExportStore {vcs store} { debug.m/web/site {} set export [m vcs export $vcs $store] if {$export ne {}} { set path external/local_${store} WX static/$path $export set export [LB $path {Local Site}] } return $export } proc ::m::web::site::StoreForks {pname url store serial forks} { debug.m/web/site {} set series [m store getx $forks] set page store_${store}_forks_${serial} set up [LB store_${store}.html $url] set title "[llength $forks] [ForkLogo] of $up" ListSimple $pname $title $page.md $series return ${page}.html } proc ::m::web::site::Store {project pname store} { debug.m/web/site {} # Get page pieces ... set urls [m store remotes $store] set repos [lmap u $urls { m repo id $u }] set links [lmap r $repos { RLink $r }] set sd [m store get $store] dict with sd {} # -> size, sizep # commits, commitp # vcs # vcsname # created # changed # updated # attend # active # remote # min_sec, max_sec, win_sec set spent [StatsTime $min_sec $max_sec $win_sec] lassign [m vcs caps $store] stdout stderr set logo [T "Operation" $stdout] set loge [T "Notes & Errors" $stderr] set commits [Commits $commits $commitp] set dsize [Size $size $sizep] set export [ExportStore $vcs $store] set vcslogo [VCSLogo [m vcs code $vcs] $vcsname] # Assemble page ... append text [H $pname] append text |||| \n append text |---|---:|---| \n if {![llength $urls]} { R $vcslogo {} {} } else { set threshold 5 set left $vcslogo foreach r $repos l $links u $urls { R $left {} $l set left {} # For each repo show the forks, up to a threshold. If # there are more than that a separate page is created for # the list and linked. set forks [m repo forks $r] set nforks [llength $forks] if {$nforks} { incr m set links [lmap f $forks { OLink $f }] foreach link $links { R {} [ForkLogo] $link incr k if {$k < $threshold} continue set more [expr {$nforks - $threshold}] R {} {} [LB [StoreForks $pname $u $store $m $forks] "+ $more more"] break } unset k } } } R Size {} $dsize R Commits {} $commits if {$export ne {}} { R {} {} $export } R {Update Stats} {} $spent R {Last Change} {} [m format epoch $changed] R {Last Check} {} [set lc [m format epoch $updated]] R Created {} [m format epoch $created] append text \n append text "## Messages as of last check on $lc" \n\n append text $logo \n append text $loge \n append text \n append text [F] W pages/store_${store}.md $text return } proc ::m::web::site::Contact {} { debug.m/web/site {} append text [H Contact] append text [F] W pages/contact.md $text return } proc ::m::web::site::ListSimple {title subtitle page series} { # A cut down form of `List`. No sorting. No other stats. debug.m/web/site {} set hvcs VCS set hsize Size set hname Project set hchan Changed append text [H $title] append text $subtitle \n append text \n append text "||$hname|Repository||$hvcs|$hsize|$hchan|Updated|Created|" \n append text "|---|---|---|---|---|---:|---|---|---|" \n set fork [ForkLogo] set mname {} set last {} foreach row $series { dict with row {} # store mname vcode changed updated created size active remote attend # -- origin url set tag {} if {$created eq "."} { append text "||||||||||" \n continue } set img [StatusRefs $attend $active $remote] set size [m format size $size] set changed [m format epoch $changed] set updated [m format epoch $updated] set created [m format epoch $created] set vcode [VCSLogo $vcode $vcode] set vcode [LB store_${store}.html $vcode] if {$mname ne {}} { set mname [LB store_${store}.html $mname] } set url [LB store_${store}.html $url] if {$origin ne {}} { append tag $fork } append text "|$img|$mname|$url|$tag|$vcode|$size|$changed|$updated|$created|" \n set last $mname } append text \n\n append text [F] W pages/$page $text return } proc ::m::web::site::List {suffix page series stats} { debug.m/web/site {} dict with stats {} # issues # disabled # size # nrepos # nprojects # nstores # ccycle # pcycle append text [H "Index ($suffix)"] set hvcs [L index_vcs.html VCS ] set hsize [L index_size.html Size ] set hname [L index_name.html Project ] set hchan [L index.html Changed ] set issues [L index_issues.html "Issues: $issues" ] set disabled [L index_disabled.html "Disabled: $disabled" ] set ccf [m format epoch $ccycle] set pcf [m format epoch $pcycle] set dt [expr {$ccycle - $pcycle}] set dtf [m format interval $dt] append text "Projects: " $nprojects , append text " Repos: " $nrepos , append text " Stores: " $nstores , append text " Size: " [m format size $size] , append text " " $issues , \n append text " " $disabled \n append text \n append text "Cycles: Current began __" $ccf "__, " append text "Last began __" $pcf "__, taking __" $dtf "__" \n append text \n append text "||$hname|Repository||$hvcs|$hsize|$hchan|Updated|Created|" \n append text "|---|---|---|---|---|---:|---|---|---|" \n # Disable insertion of cycle flags for all tables but sorted by change. if {$page ne "index.md"} { set pcycle -1 set ccycle -1 } set fork [ForkLogo] set mname {} set last {} foreach row $series { dict with row {} # store mname vcode changed updated created size active remote attend # -- origin url set tag {} if {$created eq "."} { append text "||||||||||" \n continue } if {$changed ne {}} { if {$changed < $ccycle} { append text "||__$ccf Start Of Current Cycle__||||||" \n set ccycle -1 ;# Prevent further triggers } if {$changed < $pcycle} { append text "||__$pcf Start Of Last Cycle__||||||" \n set pcycle -1 ;# Prevent further triggers } } set img [StatusRefs $attend $active $remote] set size [m format size $size] set changed [m format epoch $changed] set updated [m format epoch $updated] set created [m format epoch $created] set vcode [VCSLogo $vcode $vcode] set vcode [LB store_${store}.html $vcode] if {$mname ne {}} { set mname [LB store_${store}.html $mname] } set url [LB store_${store}.html $url] if {$origin ne {}} { append tag $fork } append text "|$img|$mname|$url|$tag|$vcode|$size|$changed|$updated|$created|" \n set last $mname } append text \n\n append text [F] W pages/$page $text return } proc ::m::web::site::VCSLogo {vcode vcsname} { debug.m/web/site {} return "[IH 32 images/logo/$vcode.svg $vcsname] $vcsname" } proc ::m::web::site::ForkLogo {} { debug.m/web/site {} IH 32 images/fork.svg Fork } proc ::m::web::site::Export {} { debug.m/web/site {} W static/spec.txt [m project spec] return } proc ::m::web::site::Search {} { debug.m/web/site {} WX static/search [CGI mirror-search] return |
︙ | ︙ | |||
445 446 447 448 449 450 451 | } proc ::m::web::site::Sync {} { debug.m/web/site {} # Data flows # - Main | | | < | < | 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 | } proc ::m::web::site::Sync {} { debug.m/web/site {} # Data flows # - Main # - repo_pending local, no sync # - reply local, no sync # - rolodex local, no sync # - schema local, no sync # # - project [1] join/view pushed to site # - repository [1] store_index, total replacement # - store [1] # - version_control_system [1], plus copy to vcs # # - rejected push to site rejected, total replacement # - submission pull from site (insert or update) # - submission_handled push to site, deletions in submission # # - Site |
︙ | ︙ | |||
619 620 621 622 623 624 625 | # database. Implemented as `delete all old ; insert all new`. m site eval { DELETE FROM store_index } # m store search '' (inlined, simply all) m db eval { SELECT S.id AS store | | | | | | | < < < | < < < < < | < < < < | < | | < | | > > | | > > > > > > > > > | 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 | # database. Implemented as `delete all old ; insert all new`. m site eval { DELETE FROM store_index } # m store search '' (inlined, simply all) m db eval { SELECT S.id AS store , (SELECT max (P.name) FROM project P, repository R WHERE P.id = R.project AND R.store = S.id) AS pname , V.code AS vcode , S.changed AS changed , S.updated AS updated , S.created AS created , (SELECT sum (has_issues) FROM repository R WHERE R.store = S.id) AS attend , S.size_kb AS size , (SELECT count (*) FROM repository R WHERE R.store = S.id) AS remote , (SELECT sum (is_active) FROM repository R WHERE R.store = S.id) AS active FROM store S , version_control_system V WHERE S.vcs = V.id } { # store, pname, vcode, changed, updated, created, size, remote, active, attend set page store_${store}.html set status [StatusIcons $attend $active $remote] set remotes [m db eval { SELECT R.url FROM repository R , store S WHERE S.id = :store AND S.id = R.store }] lappend remotes $pname set remotes [string tolower [join $remotes { }]] # We are using the remotes field for the entire text we can # search over. Should rename the field, not bothering until # we need a larger schema change it can be folded into. m site eval { INSERT INTO store_index VALUES ( NULL, :pname, :vcode, :page, :remotes, :status, :size, :changed, :updated, :created ) } } # Copy the VCS information # Logically this ... if 0 {m site eval { DELETE FROM vcs ; INSERT INTO VCS SELECT id, code, name FROM version_control_system }} # It is done like below because we are operating on two databases # here. And it is simpler than to attach/detach one of the # databases into the other connection. m site eval { DELETE FROM vcs } m db eval { SELECT id , code , name FROM version_control_system } { m site eval { |
︙ | ︙ | |||
1144 1145 1146 1147 1148 1149 1150 | </svg> static/images/logo/fossil.svg<svg width="320" height="440" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><g transform="translate(-223.69964,-322.98867)" style="fill:#808080;stroke:none"><g transform="matrix(3.4464775e-2,0,0,3.4464775e-2,64.3835,244.04121)" visibility="visible" style="visibility:visible"><path d="M 7207,8311 L 7191,8307 L 7176,8299 L 7162,8289 L 7149,8276 L 7136,8260 L 7124,8242 L 7103,8197 L 7084,8142 L 7069,8078 L 7057,8006 L 7048,7926 L 7041,7744 L 7048,7538 L 7068,7312 L 7104,7073 L 7153,6836 L 7210,6617 L 7274,6421 L 7343,6252 L 7379,6179 L 7415,6115 L 7451,6061 L 7487,6016 L 7522,5981 L 7540,5967 L 7557,5957 L 7574,5949 L 7591,5944 L 7608,5943 L 7624,5944 L 7640,5948 L 7655,5956 L 7669,5966 L 7683,5979 L 7695,5995 L 7707,6013 L 7729,6058 L 7747,6113 L 7762,6177 L 7774,6249 L 7783,6329 L 7790,6511 L 7784,6717 L 7763,6943 L 7727,7182 L 7679,7419 L 7621,7638 L 7557,7834 L 7488,8003 L 7452,8075 L 7416,8139 L 7380,8194 L 7344,8239 L 7309,8274 L 7291,8287 L 7274,8298 L 7257,8306 L 7240,8310 L 7223,8312 L 7207,8311 z"/><path d="M 7607,10301 L 7592,10303 L 7576,10302 L 7560,10299 L 7544,10294 L 7527,10286 L 7511,10275 L 7477,10248 L 7442,10212 L 7408,10169 L 7373,10117 L 7339,10058 L 7272,9921 L 7210,9761 L 7154,9581 L 7106,9386 L 7070,9188 L 7048,9001 L 7040,8829 L 7045,8677 L 7052,8610 L 7063,8549 L 7077,8494 L 7094,8448 L 7114,8409 L 7125,8393 L 7136,8379 L 7149,8368 L 7162,8358 L 7176,8351 L 7191,8347 L 7206,8345 L 7222,8346 L 7238,8349 L 7254,8354 L 7271,8362 L 7288,8372 L 7322,8399 L 7356,8435 L 7391,8479 L 7425,8531 L 7460,8589 L 7526,8726 L 7589,8887 L 7645,9066 L 7693,9262 L 7729,9460 L 7750,9647 L 7758,9818 L 7753,9971 L 7746,10038 L 7735,10099 L 7721,10153 L 7704,10200 L 7684,10239 L 7673,10255 L 7662,10269 L 7649,10280 L 7636,10290 L 7622,10297 L 7607,10301 z"/><path d="M 8749,11736 L 8737,11743 L 8724,11749 L 8709,11752 L 8694,11754 L 8677,11754 L 8659,11751 L 8621,11742 L 8579,11726 L 8534,11703 L 8486,11673 L 8436,11638 L 8330,11551 L 8219,11443 L 8107,11316 L 7995,11173 L 7892,11023 L 7805,10878 L 7735,10739 L 7684,10612 L 7665,10553 L 7652,10499 L 7643,10449 L 7640,10404 L 7643,10365 L 7646,10348 L 7651,10332 L 7657,10317 L 7665,10304 L 7674,10293 L 7685,10284 L 7697,10277 L 7711,10271 L 7725,10268 L 7741,10266 L 7757,10266 L 7775,10268 L 7814,10278 L 7855,10294 L 7900,10317 L 7948,10346 L 7998,10381 L 8104,10469 L 8215,10577 L 8328,10704 L 8440,10847 L 8543,10996 L 8630,11142 L 8699,11280 L 8751,11408 L 8769,11466 L 8783,11521 L 8791,11570 L 8794,11615 L 8791,11655 L 8788,11672 L 8783,11688 L 8777,11703 L 8769,11716 L 8760,11727 L 8749,11736 z"/><path d="M 10683,12127 L 10680,12139 L 10674,12151 L 10666,12162 L 10656,12173 L 10643,12183 L 10628,12192 L 10592,12209 L 10547,12224 L 10494,12237 L 10434,12247 L 10368,12255 L 10217,12263 L 10045,12261 L 9857,12248 L 9657,12224 L 9458,12190 L 9275,12149 L 9110,12103 L 8968,12052 L 8906,12025 L 8852,11999 L 8805,11972 L 8766,11945 L 8736,11918 L 8725,11904 L 8715,11891 L 8708,11878 L 8704,11865 L 8702,11852 L 8702,11840 L 8705,11828 L 8711,11816 L 8719,11805 L 8729,11794 L 8742,11784 L 8757,11775 L 8793,11758 L 8838,11743 L 8891,11730 L 8950,11720 L 9017,11712 L 9168,11704 L 9339,11706 L 9527,11719 L 9727,11743 L 9926,11777 L 10110,11818 L 10275,11864 L 10417,11915 L 10479,11942 L 10533,11968 L 10580,11995 L 10619,12022 L 10649,12049 L 10660,12063 L 10670,12076 L 10677,12089 L 10681,12102 L 10683,12115 L 10683,12127 z"/><path d="M 10761,12053 L 10758,12043 L 10758,12032 L 10760,12021 L 10763,12009 L 10769,11996 L 10777,11983 L 10799,11955 L 10828,11925 L 10864,11894 L 10955,11826 L 11070,11755 L 11206,11682 L 11359,11610 L 11526,11540 L 11696,11478 L 11858,11427 L 12007,11389 L 12140,11363 L 12253,11350 L 12301,11349 L 12343,11351 L 12378,11357 L 12392,11361 L 12405,11367 L 12416,11373 L 12425,11380 L 12432,11388 L 12437,11397 L 12440,11407 L 12440,11418 L 12438,11429 L 12435,11441 L 12429,11454 L 12421,11467 L 12399,11495 L 12370,11525 L 12334,11556 L 12243,11624 L 12127,11695 L 11992,11768 L 11838,11840 L 11671,11910 L 11501,11972 L 11340,12023 L 11191,12061 L 11058,12087 L 10945,12100 L 10897,12101 L 10855,12099 L 10820,12093 L 10806,12089 L 10793,12083 L 10782,12077 L 10773,12070 L 10766,12062 L 10761,12053 z"/><path d="M 12410,11353 L 12408,11351 L 12406,11349 L 12404,11344 L 12402,11337 L 12402,11330 L 12402,11322 L 12403,11312 L 12409,11291 L 12418,11267 L 12430,11239 L 12465,11175 L 12511,11102 L 12568,11022 L 12635,10936 L 12710,10847 L 12788,10761 L 12864,10683 L 12937,10616 L 13003,10560 L 13061,10518 L 13087,10502 L 13110,10490 L 13130,10482 L 13139,10479 L 13147,10478 L 13154,10477 L 13161,10478 L 13166,10480 L 13169,10481 L 13171,10483 L 13173,10485 L 13175,10487 L 13177,10492 L 13179,10499 L 13180,10506 L 13179,10514 L 13178,10524 L 13173,10545 L 13164,10569 L 13152,10597 L 13117,10661 L 13071,10734 L 13014,10814 L 12947,10900 L 12872,10989 L 12794,11075 L 12718,11153 L 12645,11220 L 12579,11276 L 12521,11318 L 12495,11334 L 12472,11346 L 12451,11354 L 12442,11357 L 12434,11358 L 12427,11359 L 12420,11358 L 12415,11356 L 12412,11355 L 12410,11353 z"/><path d="M 8102,11826 L 8102,11791 L 8101,11755 L 8100,11720 L 8098,11685 L 8096,11651 L 8093,11617 L 8089,11583 L 8086,11550 L 8081,11518 L 8077,11487 L 8072,11456 L 8066,11427 L 8060,11398 L 8054,11371 L 8047,11344 L 8039,11319 L 8032,11296 L 8024,11273 L 8016,11252 L 8007,11233 L 7999,11215 L 7990,11198 L 7980,11184 L 7971,11171 L 7961,11159 L 7951,11149 L 7941,11141 L 7931,11135 L 7921,11131 L 7911,11128 L 7901,11127 L 7901,11127 L 7891,11128 L 7881,11131 L 7871,11135 L 7861,11141 L 7851,11149 L 7841,11159 L 7831,11171 L 7822,11184 L 7812,11198 L 7803,11215 L 7795,11233 L 7786,11252 L 7778,11273 L 7770,11296 L 7763,11319 L 7755,11344 L 7748,11371 L 7742,11398 L 7736,11427 L 7730,11456 L 7725,11487 L 7721,11518 L 7716,11550 L 7713,11583 L 7709,11617 L 7706,11651 L 7704,11685 L 7702,11720 L 7701,11755 L 7700,11791 L 7700,11826 L 7700,11826 L 7700,11861 L 7701,11897 L 7702,11932 L 7704,11967 L 7706,12001 L 7709,12035 L 7713,12069 L 7716,12102 L 7721,12134 L 7725,12165 L 7730,12196 L 7736,12225 L 7742,12254 L 7748,12281 L 7755,12308 L 7763,12333 L 7770,12356 L 7778,12379 L 7786,12400 L 7795,12419 L 7803,12437 L 7812,12454 L 7822,12468 L 7831,12481 L 7841,12493 L 7851,12503 L 7861,12511 L 7871,12517 L 7881,12521 L 7891,12524 L 7901,12525 L 7901,12525 L 7911,12524 L 7921,12521 L 7931,12517 L 7941,12511 L 7951,12503 L 7961,12493 L 7971,12481 L 7980,12468 L 7990,12454 L 7999,12437 L 8007,12419 L 8016,12400 L 8024,12379 L 8032,12356 L 8039,12333 L 8047,12308 L 8054,12281 L 8060,12254 L 8066,12225 L 8072,12196 L 8077,12165 L 8081,12134 L 8086,12102 L 8089,12069 L 8093,12035 L 8096,12001 L 8098,11967 L 8100,11932 L 8101,11897 L 8102,11861 L 8102,11826 z"/><path d="M 7825,12576 L 7819,12584 L 7810,12591 L 7801,12597 L 7789,12601 L 7777,12604 L 7762,12606 L 7730,12607 L 7692,12603 L 7649,12595 L 7602,12583 L 7551,12567 L 7438,12522 L 7313,12463 L 7181,12391 L 7043,12306 L 6910,12215 L 6790,12123 L 6685,12033 L 6599,11948 L 6563,11907 L 6533,11869 L 6508,11833 L 6490,11800 L 6477,11770 L 6473,11756 L 6471,11743 L 6470,11731 L 6471,11719 L 6474,11709 L 6479,11700 L 6486,11692 L 6494,11685 L 6504,11679 L 6515,11675 L 6528,11672 L 6542,11670 L 6575,11669 L 6613,11673 L 6656,11681 L 6703,11693 L 6754,11709 L 6867,11753 L 6992,11812 L 7124,11884 L 7262,11969 L 7395,12061 L 7515,12153 L 7619,12243 L 7706,12328 L 7741,12369 L 7771,12407 L 7796,12443 L 7815,12476 L 7827,12506 L 7831,12520 L 7834,12533 L 7834,12545 L 7833,12557 L 7830,12567 L 7825,12576 z"/><path d="M 6460,11695 L 6457,11697 L 6454,11699 L 6451,11701 L 6447,11702 L 6443,11703 L 6438,11703 L 6428,11702 L 6416,11700 L 6403,11696 L 6389,11691 L 6374,11684 L 6342,11666 L 6307,11643 L 6270,11616 L 6233,11584 L 6197,11550 L 6166,11517 L 6139,11485 L 6118,11455 L 6110,11441 L 6103,11427 L 6098,11415 L 6094,11404 L 6092,11393 L 6092,11389 L 6092,11385 L 6093,11381 L 6094,11377 L 6096,11374 L 6098,11371 L 6101,11369 L 6104,11366 L 6107,11365 L 6111,11364 L 6115,11363 L 6120,11363 L 6130,11363 L 6142,11366 L 6154,11370 L 6168,11375 L 6183,11382 L 6216,11399 L 6251,11422 L 6288,11450 L 6325,11481 L 6361,11515 L 6392,11548 L 6418,11581 L 6439,11611 L 6448,11625 L 6455,11638 L 6460,11651 L 6464,11662 L 6465,11672 L 6466,11677 L 6465,11681 L 6465,11685 L 6464,11689 L 6462,11692 L 6460,11695 z"/><path d="M 13184,10437 L 13182,10436 L 13179,10434 L 13175,10430 L 13171,10424 L 13168,10418 L 13166,10410 L 13164,10401 L 13163,10379 L 13164,10353 L 13167,10322 L 13179,10251 L 13200,10167 L 13229,10073 L 13266,9970 L 13309,9862 L 13357,9756 L 13405,9659 L 13453,9572 L 13498,9499 L 13540,9440 L 13560,9416 L 13578,9398 L 13595,9384 L 13602,9378 L 13610,9374 L 13616,9372 L 13623,9370 L 13629,9371 L 13631,9371 L 13634,9372 L 13636,9373 L 13639,9375 L 13643,9379 L 13646,9385 L 13649,9391 L 13651,9399 L 13653,9408 L 13655,9430 L 13654,9456 L 13651,9487 L 13638,9558 L 13617,9642 L 13588,9736 L 13551,9839 L 13508,9947 L 13461,10053 L 13413,10150 L 13365,10237 L 13320,10311 L 13278,10369 L 13258,10393 L 13240,10411 L 13223,10425 L 13216,10431 L 13208,10435 L 13202,10437 L 13195,10439 L 13189,10438 L 13187,10438 L 13184,10437 z"/><path d="M 10098,10825 L 10098,10790 L 10097,10754 L 10096,10719 L 10094,10684 L 10092,10650 L 10089,10616 L 10086,10582 L 10082,10549 L 10078,10517 L 10073,10486 L 10068,10455 L 10062,10426 L 10056,10397 L 10050,10370 L 10043,10343 L 10036,10318 L 10029,10295 L 10021,10272 L 10013,10251 L 10004,10232 L 9996,10214 L 9987,10197 L 9977,10183 L 9968,10170 L 9959,10158 L 9949,10148 L 9939,10140 L 9929,10134 L 9919,10130 L 9909,10127 L 9899,10126 L 9899,10126 L 9889,10127 L 9879,10130 L 9869,10134 L 9859,10140 L 9849,10148 L 9839,10158 L 9830,10170 L 9821,10183 L 9811,10197 L 9802,10214 L 9794,10232 L 9785,10251 L 9777,10272 L 9769,10295 L 9762,10318 L 9755,10343 L 9748,10370 L 9742,10397 L 9736,10426 L 9730,10455 L 9725,10486 L 9720,10517 L 9716,10549 L 9712,10582 L 9709,10616 L 9706,10650 L 9704,10684 L 9702,10719 L 9701,10754 L 9700,10790 L 9700,10825 L 9700,10825 L 9700,10860 L 9701,10896 L 9702,10931 L 9704,10966 L 9706,11000 L 9709,11034 L 9712,11068 L 9716,11101 L 9720,11133 L 9725,11164 L 9730,11195 L 9736,11224 L 9742,11253 L 9748,11280 L 9755,11307 L 9762,11332 L 9769,11355 L 9777,11378 L 9785,11399 L 9794,11418 L 9802,11436 L 9811,11453 L 9821,11467 L 9830,11480 L 9839,11492 L 9849,11502 L 9859,11510 L 9869,11516 L 9879,11520 L 9889,11523 L 9899,11524 L 9899,11524 L 9909,11523 L 9919,11520 L 9929,11516 L 9939,11510 L 9949,11502 L 9959,11492 L 9968,11480 L 9977,11467 L 9987,11453 L 9996,11436 L 10004,11418 L 10013,11399 L 10021,11378 L 10029,11355 L 10036,11332 L 10043,11307 L 10050,11280 L 10056,11253 L 10062,11224 L 10068,11195 L 10073,11164 L 10078,11133 L 10082,11101 L 10086,11068 L 10089,11034 L 10092,11000 L 10094,10966 L 10096,10931 L 10097,10896 L 10098,10860 L 10098,10825 z"/><path d="M 9827,11575 L 9821,11583 L 9812,11590 L 9803,11596 L 9791,11600 L 9779,11603 L 9764,11605 L 9732,11606 L 9694,11602 L 9651,11594 L 9604,11582 L 9553,11566 L 9440,11521 L 9315,11462 L 9183,11390 L 9045,11305 L 8912,11214 L 8792,11122 L 8687,11032 L 8601,10947 L 8565,10906 L 8535,10868 L 8510,10832 L 8492,10799 L 8479,10769 L 8475,10755 L 8473,10742 L 8472,10730 L 8473,10718 L 8476,10708 L 8481,10699 L 8488,10691 L 8496,10684 L 8506,10678 L 8517,10674 L 8530,10671 L 8544,10669 L 8577,10668 L 8615,10672 L 8658,10680 L 8705,10692 L 8756,10708 L 8869,10752 L 8994,10811 L 9126,10883 L 9264,10968 L 9397,11060 L 9517,11152 L 9621,11242 L 9708,11327 L 9743,11368 L 9773,11406 L 9798,11442 L 9817,11475 L 9829,11505 L 9833,11519 L 9836,11532 L 9836,11544 L 9835,11556 L 9832,11566 L 9827,11575 z"/><path d="M 6085,9230 L 6075,9220 L 6067,9209 L 6060,9197 L 6055,9184 L 6050,9169 L 6047,9154 L 6045,9120 L 6048,9083 L 6055,9042 L 6067,8998 L 6083,8952 L 6104,8904 L 6128,8853 L 6190,8749 L 6266,8641 L 6357,8533 L 6456,8432 L 6555,8346 L 6653,8274 L 6701,8245 L 6747,8220 L 6791,8199 L 6833,8183 L 6873,8172 L 6910,8165 L 6944,8164 L 6960,8166 L 6975,8169 L 6989,8173 L 7001,8179 L 7013,8186 L 7024,8194 L 7034,8204 L 7042,8215 L 7049,8227 L 7054,8241 L 7059,8255 L 7062,8271 L 7064,8304 L 7062,8342 L 7054,8383 L 7043,8426 L 7027,8473 L 7006,8521 L 6981,8571 L 6920,8676 L 6843,8784 L 6753,8892 L 6654,8993 L 6554,9079 L 6456,9151 L 6409,9180 L 6362,9205 L 6318,9226 L 6276,9242 L 6236,9253 L 6199,9259 L 6165,9260 L 6149,9259 L 6134,9256 L 6120,9251 L 6108,9246 L 6096,9239 L 6085,9230 z"/><path d="M 5910,9183 L 5900,9185 L 5890,9184 L 5879,9182 L 5868,9177 L 5856,9171 L 5845,9163 L 5820,9141 L 5795,9113 L 5769,9078 L 5743,9037 L 5716,8991 L 5663,8882 L 5611,8754 L 5561,8612 L 5516,8456 L 5479,8299 L 5451,8150 L 5434,8014 L 5427,7893 L 5427,7839 L 5430,7791 L 5435,7748 L 5443,7711 L 5454,7680 L 5460,7667 L 5467,7656 L 5474,7647 L 5483,7639 L 5491,7634 L 5501,7630 L 5511,7628 L 5521,7629 L 5532,7631 L 5543,7636 L 5555,7642 L 5566,7650 L 5591,7671 L 5616,7700 L 5642,7735 L 5668,7776 L 5695,7822 L 5748,7931 L 5800,8058 L 5850,8201 L 5895,8357 L 5932,8514 L 5960,8663 L 5977,8799 L 5984,8920 L 5984,8974 L 5981,9022 L 5975,9065 L 5967,9102 L 5957,9133 L 5951,9146 L 5944,9157 L 5936,9166 L 5928,9174 L 5919,9179 L 5910,9183 z"/><path d="M 8630,9344 L 8623,9336 L 8617,9328 L 8613,9318 L 8609,9306 L 8607,9294 L 8607,9281 L 8609,9251 L 8615,9217 L 8626,9180 L 8642,9140 L 8662,9097 L 8713,9004 L 8779,8903 L 8858,8798 L 8950,8691 L 9048,8589 L 9144,8500 L 9238,8425 L 9325,8365 L 9366,8341 L 9405,8321 L 9440,8306 L 9473,8296 L 9503,8291 L 9516,8291 L 9529,8291 L 9540,8294 L 9550,8297 L 9560,8302 L 9568,8308 L 9575,8316 L 9581,8325 L 9585,8335 L 9588,8346 L 9590,8358 L 9591,8372 L 9589,8401 L 9582,8435 L 9571,8472 L 9556,8512 L 9536,8555 L 9485,8648 L 9419,8749 L 9339,8854 L 9248,8961 L 9150,9063 L 9054,9152 L 8960,9228 L 8873,9288 L 8832,9312 L 8793,9331 L 8758,9346 L 8725,9356 L 8695,9361 L 8682,9362 L 8669,9361 L 8658,9359 L 8648,9355 L 8638,9350 L 8630,9344 z"/><path d="M 8566,9557 L 8557,9563 L 8547,9566 L 8536,9568 L 8524,9569 L 8511,9568 L 8497,9565 L 8465,9555 L 8431,9539 L 8393,9517 L 8353,9490 L 8310,9458 L 8218,9378 L 8120,9282 L 8019,9170 L 7917,9044 L 7821,8913 L 7739,8787 L 7670,8668 L 7617,8558 L 7597,8509 L 7581,8462 L 7569,8420 L 7562,8383 L 7561,8350 L 7562,8336 L 7564,8323 L 7567,8311 L 7572,8301 L 7578,8292 L 7586,8285 L 7595,8279 L 7605,8276 L 7616,8274 L 7628,8273 L 7641,8274 L 7655,8277 L 7687,8287 L 7721,8303 L 7759,8325 L 7799,8352 L 7842,8385 L 7934,8464 L 8032,8561 L 8133,8673 L 8235,8799 L 8330,8930 L 8413,9056 L 8482,9175 L 8535,9285 L 8555,9334 L 8571,9380 L 8583,9422 L 8589,9460 L 8591,9492 L 8590,9507 L 8588,9520 L 8585,9531 L 8580,9541 L 8574,9550 L 8566,9557 z"/><path d="M 6578,11626 L 6575,11627 L 6572,11627 L 6569,11627 L 6565,11626 L 6561,11624 L 6557,11622 L 6549,11616 L 6540,11608 L 6531,11598 L 6521,11586 L 6510,11573 L 6489,11541 L 6467,11503 L 6445,11460 L 6424,11413 L 6405,11365 L 6390,11319 L 6379,11277 L 6371,11239 L 6369,11222 L 6367,11207 L 6367,11193 L 6367,11181 L 6369,11171 L 6370,11166 L 6372,11163 L 6374,11159 L 6376,11157 L 6378,11155 L 6381,11153 L 6384,11152 L 6387,11152 L 6391,11152 L 6394,11153 L 6398,11155 L 6402,11157 L 6411,11163 L 6420,11171 L 6429,11181 L 6439,11193 L 6449,11206 L 6471,11238 L 6493,11276 L 6514,11319 L 6535,11366 L 6554,11414 L 6569,11460 L 6581,11502 L 6588,11540 L 6591,11557 L 6592,11572 L 6593,11586 L 6592,11598 L 6590,11608 L 6589,11613 L 6587,11616 L 6585,11620 L 6583,11622 L 6581,11624 L 6578,11626 z"/><path d="M 5952,11673 L 5953,11670 L 5955,11667 L 5957,11665 L 5960,11663 L 5963,11660 L 5967,11658 L 5977,11655 L 5989,11652 L 6003,11651 L 6037,11649 L 6077,11651 L 6122,11655 L 6172,11663 L 6224,11674 L 6276,11687 L 6323,11701 L 6366,11717 L 6402,11733 L 6432,11748 L 6444,11756 L 6454,11764 L 6461,11771 L 6464,11775 L 6466,11778 L 6468,11781 L 6469,11785 L 6469,11788 L 6469,11791 L 6468,11794 L 6466,11797 L 6464,11799 L 6461,11802 L 6458,11804 L 6454,11806 L 6444,11809 L 6432,11812 L 6418,11814 L 6384,11815 L 6344,11814 L 6299,11809 L 6249,11802 L 6197,11791 L 6145,11778 L 6098,11763 L 6055,11748 L 6019,11732 L 5989,11716 L 5977,11708 L 5967,11701 L 5960,11693 L 5957,11690 L 5955,11686 L 5953,11683 L 5952,11679 L 5952,11676 L 5952,11673 z"/><path d="M 5384,7616 L 5381,7618 L 5378,7620 L 5375,7622 L 5371,7623 L 5367,7624 L 5362,7624 L 5352,7623 L 5340,7621 L 5327,7617 L 5313,7612 L 5298,7605 L 5266,7587 L 5231,7564 L 5194,7537 L 5157,7505 L 5121,7471 L 5090,7438 L 5063,7406 L 5042,7376 L 5034,7362 L 5027,7348 L 5022,7336 L 5018,7325 L 5016,7314 L 5016,7310 L 5016,7306 L 5017,7302 L 5018,7298 L 5020,7295 L 5022,7292 L 5025,7290 L 5028,7287 L 5031,7286 L 5035,7285 L 5039,7284 L 5044,7284 L 5054,7284 L 5066,7287 L 5078,7291 L 5092,7296 L 5107,7303 L 5140,7320 L 5175,7343 L 5212,7371 L 5249,7402 L 5285,7436 L 5316,7469 L 5342,7502 L 5363,7532 L 5372,7546 L 5379,7559 L 5384,7572 L 5388,7583 L 5389,7593 L 5390,7598 L 5389,7602 L 5389,7606 L 5388,7610 L 5386,7613 L 5384,7616 z"/><path d="M 5502,7547 L 5499,7548 L 5496,7548 L 5493,7548 L 5489,7547 L 5485,7545 L 5481,7543 L 5473,7537 L 5464,7529 L 5455,7519 L 5445,7507 L 5434,7494 L 5413,7462 L 5391,7424 L 5369,7381 L 5348,7334 L 5329,7286 L 5314,7240 L 5303,7198 L 5295,7160 L 5293,7143 L 5291,7128 L 5291,7114 L 5291,7102 L 5293,7092 L 5294,7087 L 5296,7084 L 5298,7080 L 5300,7078 L 5302,7076 L 5305,7074 L 5308,7073 L 5311,7073 L 5315,7073 L 5318,7074 L 5322,7076 L 5326,7078 L 5335,7084 L 5344,7092 L 5353,7102 L 5363,7114 L 5373,7127 L 5395,7159 L 5417,7197 L 5438,7240 L 5459,7287 L 5478,7335 L 5493,7381 L 5505,7423 L 5512,7461 L 5515,7478 L 5516,7493 L 5517,7507 L 5516,7519 L 5514,7529 L 5513,7534 L 5511,7537 L 5509,7541 L 5507,7543 L 5505,7545 L 5502,7547 z"/><path d="M 4875,7594 L 4876,7591 L 4878,7588 L 4880,7586 L 4883,7584 L 4886,7581 L 4890,7579 L 4900,7576 L 4912,7573 L 4926,7572 L 4960,7570 L 5000,7572 L 5045,7576 L 5095,7584 L 5147,7594 L 5199,7607 L 5246,7622 L 5289,7637 L 5325,7653 L 5355,7669 L 5367,7677 L 5377,7684 L 5384,7692 L 5387,7695 L 5389,7699 L 5391,7702 L 5392,7706 L 5392,7709 L 5392,7712 L 5391,7715 L 5389,7718 L 5387,7720 L 5384,7722 L 5381,7725 L 5377,7727 L 5367,7730 L 5355,7733 L 5341,7734 L 5307,7736 L 5267,7734 L 5222,7730 L 5172,7722 L 5120,7711 L 5068,7698 L 5021,7684 L 4978,7668 L 4942,7653 L 4912,7637 L 4900,7629 L 4890,7621 L 4883,7614 L 4880,7610 L 4878,7607 L 4876,7604 L 4875,7600 L 4875,7597 L 4875,7594 z"/><path d="M 9763,8248 L 9761,8245 L 9759,8242 L 9758,8238 L 9758,8234 L 9757,8230 L 9758,8226 L 9759,8215 L 9763,8204 L 9768,8192 L 9775,8178 L 9784,8164 L 9805,8134 L 9831,8102 L 9862,8069 L 9897,8035 L 9934,8004 L 9970,7976 L 10005,7953 L 10037,7936 L 10052,7929 L 10066,7923 L 10079,7919 L 10090,7917 L 10100,7916 L 10105,7916 L 10109,7917 L 10113,7918 L 10116,7920 L 10119,7922 L 10122,7924 L 10124,7927 L 10126,7930 L 10127,7933 L 10127,7937 L 10128,7941 L 10127,7946 L 10126,7956 L 10122,7967 L 10117,7979 L 10110,7993 L 10102,8007 L 10080,8037 L 10054,8069 L 10023,8102 L 9988,8136 L 9951,8167 L 9914,8195 L 9880,8218 L 9847,8236 L 9833,8242 L 9819,8248 L 9806,8252 L 9794,8254 L 9784,8255 L 9780,8255 L 9775,8255 L 9772,8254 L 9768,8252 L 9765,8250 L 9763,8248 z"/><path d="M 9639,8179 L 9636,8177 L 9634,8175 L 9632,8173 L 9630,8169 L 9628,8166 L 9627,8161 L 9625,8151 L 9624,8139 L 9625,8126 L 9626,8110 L 9629,8093 L 9636,8056 L 9648,8013 L 9663,7968 L 9682,7920 L 9703,7873 L 9725,7830 L 9747,7792 L 9768,7760 L 9779,7746 L 9789,7734 L 9798,7724 L 9807,7717 L 9815,7711 L 9819,7709 L 9823,7707 L 9827,7706 L 9830,7706 L 9833,7706 L 9836,7707 L 9839,7708 L 9841,7710 L 9843,7713 L 9845,7716 L 9847,7720 L 9848,7724 L 9850,7734 L 9851,7746 L 9850,7760 L 9849,7775 L 9846,7792 L 9839,7830 L 9827,7872 L 9812,7918 L 9793,7966 L 9772,8013 L 9750,8056 L 9728,8094 L 9707,8126 L 9697,8140 L 9687,8152 L 9677,8162 L 9668,8169 L 9660,8175 L 9656,8177 L 9652,8179 L 9648,8180 L 9645,8180 L 9642,8180 L 9639,8179 z"/><path d="M 10269,8228 L 10269,8231 L 10269,8234 L 10268,8237 L 10267,8241 L 10264,8244 L 10262,8248 L 10254,8255 L 10244,8263 L 10232,8270 L 10203,8286 L 10166,8302 L 10123,8317 L 10076,8331 L 10024,8344 L 9972,8355 L 9922,8362 L 9877,8367 L 9837,8368 L 9803,8367 L 9789,8365 L 9777,8362 L 9767,8359 L 9763,8357 L 9760,8355 L 9757,8352 L 9755,8350 L 9753,8347 L 9752,8344 L 9752,8341 L 9752,8338 L 9753,8334 L 9755,8331 L 9757,8327 L 9760,8324 L 9767,8316 L 9777,8309 L 9789,8301 L 9803,8293 L 9819,8285 L 9855,8269 L 9898,8254 L 9946,8240 L 9998,8227 L 10050,8217 L 10099,8209 L 10144,8205 L 10184,8204 L 10218,8205 L 10232,8207 L 10244,8210 L 10254,8213 L 10258,8215 L 10261,8217 L 10264,8220 L 10266,8222 L 10268,8225 L 10269,8228 z"/><path d="M 9749,10085 L 9746,10087 L 9743,10089 L 9740,10091 L 9736,10092 L 9732,10093 L 9727,10093 L 9717,10092 L 9705,10090 L 9693,10086 L 9679,10081 L 9664,10074 L 9631,10056 L 9597,10034 L 9560,10006 L 9523,9975 L 9488,9941 L 9457,9908 L 9430,9876 L 9409,9846 L 9401,9832 L 9394,9819 L 9388,9806 L 9385,9795 L 9383,9785 L 9382,9780 L 9383,9776 L 9383,9772 L 9384,9768 L 9386,9765 L 9388,9762 L 9391,9760 L 9394,9758 L 9397,9756 L 9401,9755 L 9405,9754 L 9410,9754 L 9421,9755 L 9432,9757 L 9445,9761 L 9459,9767 L 9474,9774 L 9507,9791 L 9541,9814 L 9578,9841 L 9615,9872 L 9650,9906 L 9681,9939 L 9707,9971 L 9729,10001 L 9737,10015 L 9744,10029 L 9749,10041 L 9753,10052 L 9754,10063 L 9755,10067 L 9754,10071 L 9754,10075 L 9753,10079 L 9751,10082 L 9749,10085 z"/><path d="M 9868,10017 L 9865,10018 L 9862,10018 L 9859,10018 L 9855,10017 L 9852,10015 L 9848,10013 L 9840,10007 L 9831,9999 L 9821,9989 L 9801,9963 L 9780,9931 L 9758,9893 L 9736,9850 L 9715,9803 L 9696,9755 L 9681,9710 L 9670,9667 L 9662,9630 L 9658,9597 L 9657,9584 L 9658,9572 L 9660,9562 L 9661,9557 L 9662,9554 L 9664,9550 L 9666,9548 L 9668,9546 L 9671,9544 L 9674,9543 L 9677,9543 L 9680,9543 L 9684,9544 L 9688,9546 L 9692,9548 L 9700,9554 L 9709,9562 L 9718,9572 L 9728,9584 L 9739,9598 L 9760,9630 L 9782,9668 L 9803,9711 L 9824,9758 L 9843,9806 L 9858,9851 L 9870,9894 L 9878,9931 L 9880,9948 L 9882,9964 L 9882,9977 L 9882,9989 L 9880,9999 L 9879,10004 L 9877,10007 L 9875,10011 L 9873,10013 L 9871,10015 L 9868,10017 z"/><path d="M 9242,10063 L 9243,10060 L 9245,10057 L 9247,10055 L 9250,10053 L 9253,10050 L 9257,10048 L 9267,10045 L 9279,10042 L 9293,10041 L 9327,10039 L 9367,10041 L 9412,10045 L 9462,10053 L 9514,10064 L 9566,10077 L 9613,10091 L 9656,10107 L 9692,10123 L 9722,10138 L 9734,10146 L 9744,10154 L 9751,10161 L 9754,10165 L 9756,10168 L 9758,10171 L 9759,10175 L 9759,10178 L 9759,10181 L 9758,10184 L 9756,10187 L 9754,10189 L 9751,10192 L 9748,10194 L 9744,10196 L 9734,10199 L 9722,10202 L 9708,10204 L 9674,10205 L 9634,10204 L 9589,10199 L 9539,10192 L 9487,10181 L 9435,10168 L 9388,10153 L 9345,10138 L 9309,10122 L 9279,10106 L 9267,10098 L 9257,10091 L 9250,10083 L 9247,10080 L 9245,10076 L 9243,10073 L 9242,10069 L 9242,10066 L 9242,10063 z"/><path d="M 6841,4401 L 6832,4382 L 6827,4362 L 6826,4339 L 6828,4314 L 6834,4288 L 6843,4260 L 6872,4199 L 6914,4133 L 6968,4061 L 7035,3985 L 7112,3904 L 7298,3734 L 7521,3555 L 7777,3372 L 8060,3190 L 8352,3022 L 8632,2879 L 8894,2763 L 9130,2676 L 9237,2644 L 9336,2621 L 9424,2606 L 9503,2599 L 9570,2601 L 9599,2606 L 9625,2613 L 9648,2622 L 9667,2633 L 9683,2648 L 9696,2664 L 9705,2683 L 9710,2703 L 9711,2726 L 9709,2751 L 9703,2777 L 9694,2805 L 9665,2866 L 9623,2932 L 9569,3004 L 9502,3080 L 9425,3161 L 9239,3331 L 9016,3510 L 8760,3693 L 8477,3875 L 8185,4043 L 7905,4186 L 7643,4302 L 7407,4389 L 7300,4420 L 7201,4444 L 7113,4459 L 7034,4466 L 6967,4464 L 6938,4459 L 6912,4452 L 6889,4443 L 6870,4432 L 6854,4417 L 6841,4401 z"/><path d="M 9098,6041 L 9075,6069 L 9049,6094 L 9020,6117 L 8989,6137 L 8956,6155 L 8920,6170 L 8843,6193 L 8757,6206 L 8665,6209 L 8567,6203 L 8463,6188 L 8354,6163 L 8242,6129 L 8127,6087 L 8009,6035 L 7890,5975 L 7770,5907 L 7651,5830 L 7532,5745 L 7418,5654 L 7311,5560 L 7212,5464 L 7122,5366 L 7040,5267 L 6968,5168 L 6904,5069 L 6851,4972 L 6808,4876 L 6775,4783 L 6753,4694 L 6742,4608 L 6743,4527 L 6748,4489 L 6755,4452 L 6766,4417 L 6780,4383 L 6798,4351 L 6818,4321 L 6841,4293 L 6867,4268 L 6896,4245 L 6927,4225 L 6960,4207 L 6996,4192 L 7073,4169 L 7159,4156 L 7251,4152 L 7349,4158 L 7453,4174 L 7562,4199 L 7674,4232 L 7789,4275 L 7907,4326 L 8026,4387 L 8146,4455 L 8265,4532 L 8384,4617 L 8498,4708 L 8605,4802 L 8704,4898 L 8794,4996 L 8876,5095 L 8948,5194 L 9012,5293 L 9065,5390 L 9108,5486 L 9141,5579 L 9163,5668 L 9174,5754 L 9173,5835 L 9168,5873 L 9161,5910 L 9150,5945 L 9136,5979 L 9118,6011 L 9098,6041 z"/><path d="M 7879,5222 L 7868,5212 L 7858,5199 L 7851,5184 L 7845,5167 L 7841,5147 L 7839,5126 L 7841,5077 L 7850,5020 L 7866,4957 L 7888,4888 L 7917,4812 L 7992,4647 L 8091,4466 L 8210,4274 L 8348,4075 L 8496,3883 L 8643,3712 L 8786,3563 L 8921,3442 L 8984,3392 L 9044,3349 L 9099,3316 L 9150,3290 L 9196,3274 L 9217,3269 L 9237,3267 L 9255,3267 L 9272,3270 L 9287,3275 L 9300,3283 L 9311,3293 L 9321,3306 L 9329,3321 L 9334,3338 L 9338,3358 L 9340,3379 L 9338,3428 L 9329,3484 L 9314,3547 L 9291,3617 L 9263,3692 L 9187,3858 L 9089,4039 L 8970,4231 L 8832,4430 L 8684,4621 L 8536,4793 L 8393,4941 L 8258,5063 L 8195,5113 L 8136,5155 L 8080,5189 L 8029,5214 L 7983,5231 L 7962,5235 L 7942,5238 L 7924,5238 L 7907,5235 L 7892,5230 L 7879,5222 z"/><path d="M 9004,6100 L 8984,6094 L 8965,6084 L 8948,6069 L 8931,6050 L 8915,6027 L 8901,6001 L 8875,5936 L 8854,5857 L 8837,5764 L 8825,5660 L 8818,5543 L 8817,5279 L 8834,4980 L 8871,4652 L 8927,4303 L 8999,3957 L 9082,3638 L 9173,3352 L 9269,3106 L 9317,2999 L 9366,2906 L 9415,2825 L 9463,2759 L 9510,2708 L 9533,2688 L 9556,2672 L 9578,2661 L 9600,2653 L 9621,2651 L 9642,2652 L 9662,2658 L 9681,2668 L 9699,2683 L 9715,2702 L 9731,2725 L 9746,2752 L 9771,2816 L 9793,2895 L 9809,2988 L 9821,3093 L 9829,3209 L 9830,3473 L 9812,3773 L 9775,4100 L 9719,4449 L 9647,4795 L 9564,5114 L 9473,5400 L 9377,5647 L 9329,5753 L 9280,5846 L 9231,5927 L 9183,5993 L 9136,6044 L 9113,6064 L 9090,6080 L 9068,6091 L 9046,6099 L 9025,6101 L 9004,6100 z"/></g></g></svg> static/images/ok.svg<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width='32' height='32' viewBox='0 0 100 100'><circle cx='50' cy='50' r='40' fill='green'/></svg> static/images/bad.svg<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width='32' height='32' viewBox='0 0 100 100'><circle cx='50' cy='50' r='40' fill='red'/></svg> static/images/off.svg<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width='32' height='32' viewBox='0 0 100 100'><circle cx='50' cy='50' r='40' fill='black'/></svg> static/images/yellow.svg<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width='32' height='32' viewBox='0 0 100 100'><circle cx='50' cy='50' r='40' fill='yellow'/></svg> | > > > > | 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 | </svg> static/images/logo/fossil.svg<svg width="320" height="440" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><g transform="translate(-223.69964,-322.98867)" style="fill:#808080;stroke:none"><g transform="matrix(3.4464775e-2,0,0,3.4464775e-2,64.3835,244.04121)" visibility="visible" style="visibility:visible"><path d="M 7207,8311 L 7191,8307 L 7176,8299 L 7162,8289 L 7149,8276 L 7136,8260 L 7124,8242 L 7103,8197 L 7084,8142 L 7069,8078 L 7057,8006 L 7048,7926 L 7041,7744 L 7048,7538 L 7068,7312 L 7104,7073 L 7153,6836 L 7210,6617 L 7274,6421 L 7343,6252 L 7379,6179 L 7415,6115 L 7451,6061 L 7487,6016 L 7522,5981 L 7540,5967 L 7557,5957 L 7574,5949 L 7591,5944 L 7608,5943 L 7624,5944 L 7640,5948 L 7655,5956 L 7669,5966 L 7683,5979 L 7695,5995 L 7707,6013 L 7729,6058 L 7747,6113 L 7762,6177 L 7774,6249 L 7783,6329 L 7790,6511 L 7784,6717 L 7763,6943 L 7727,7182 L 7679,7419 L 7621,7638 L 7557,7834 L 7488,8003 L 7452,8075 L 7416,8139 L 7380,8194 L 7344,8239 L 7309,8274 L 7291,8287 L 7274,8298 L 7257,8306 L 7240,8310 L 7223,8312 L 7207,8311 z"/><path d="M 7607,10301 L 7592,10303 L 7576,10302 L 7560,10299 L 7544,10294 L 7527,10286 L 7511,10275 L 7477,10248 L 7442,10212 L 7408,10169 L 7373,10117 L 7339,10058 L 7272,9921 L 7210,9761 L 7154,9581 L 7106,9386 L 7070,9188 L 7048,9001 L 7040,8829 L 7045,8677 L 7052,8610 L 7063,8549 L 7077,8494 L 7094,8448 L 7114,8409 L 7125,8393 L 7136,8379 L 7149,8368 L 7162,8358 L 7176,8351 L 7191,8347 L 7206,8345 L 7222,8346 L 7238,8349 L 7254,8354 L 7271,8362 L 7288,8372 L 7322,8399 L 7356,8435 L 7391,8479 L 7425,8531 L 7460,8589 L 7526,8726 L 7589,8887 L 7645,9066 L 7693,9262 L 7729,9460 L 7750,9647 L 7758,9818 L 7753,9971 L 7746,10038 L 7735,10099 L 7721,10153 L 7704,10200 L 7684,10239 L 7673,10255 L 7662,10269 L 7649,10280 L 7636,10290 L 7622,10297 L 7607,10301 z"/><path d="M 8749,11736 L 8737,11743 L 8724,11749 L 8709,11752 L 8694,11754 L 8677,11754 L 8659,11751 L 8621,11742 L 8579,11726 L 8534,11703 L 8486,11673 L 8436,11638 L 8330,11551 L 8219,11443 L 8107,11316 L 7995,11173 L 7892,11023 L 7805,10878 L 7735,10739 L 7684,10612 L 7665,10553 L 7652,10499 L 7643,10449 L 7640,10404 L 7643,10365 L 7646,10348 L 7651,10332 L 7657,10317 L 7665,10304 L 7674,10293 L 7685,10284 L 7697,10277 L 7711,10271 L 7725,10268 L 7741,10266 L 7757,10266 L 7775,10268 L 7814,10278 L 7855,10294 L 7900,10317 L 7948,10346 L 7998,10381 L 8104,10469 L 8215,10577 L 8328,10704 L 8440,10847 L 8543,10996 L 8630,11142 L 8699,11280 L 8751,11408 L 8769,11466 L 8783,11521 L 8791,11570 L 8794,11615 L 8791,11655 L 8788,11672 L 8783,11688 L 8777,11703 L 8769,11716 L 8760,11727 L 8749,11736 z"/><path d="M 10683,12127 L 10680,12139 L 10674,12151 L 10666,12162 L 10656,12173 L 10643,12183 L 10628,12192 L 10592,12209 L 10547,12224 L 10494,12237 L 10434,12247 L 10368,12255 L 10217,12263 L 10045,12261 L 9857,12248 L 9657,12224 L 9458,12190 L 9275,12149 L 9110,12103 L 8968,12052 L 8906,12025 L 8852,11999 L 8805,11972 L 8766,11945 L 8736,11918 L 8725,11904 L 8715,11891 L 8708,11878 L 8704,11865 L 8702,11852 L 8702,11840 L 8705,11828 L 8711,11816 L 8719,11805 L 8729,11794 L 8742,11784 L 8757,11775 L 8793,11758 L 8838,11743 L 8891,11730 L 8950,11720 L 9017,11712 L 9168,11704 L 9339,11706 L 9527,11719 L 9727,11743 L 9926,11777 L 10110,11818 L 10275,11864 L 10417,11915 L 10479,11942 L 10533,11968 L 10580,11995 L 10619,12022 L 10649,12049 L 10660,12063 L 10670,12076 L 10677,12089 L 10681,12102 L 10683,12115 L 10683,12127 z"/><path d="M 10761,12053 L 10758,12043 L 10758,12032 L 10760,12021 L 10763,12009 L 10769,11996 L 10777,11983 L 10799,11955 L 10828,11925 L 10864,11894 L 10955,11826 L 11070,11755 L 11206,11682 L 11359,11610 L 11526,11540 L 11696,11478 L 11858,11427 L 12007,11389 L 12140,11363 L 12253,11350 L 12301,11349 L 12343,11351 L 12378,11357 L 12392,11361 L 12405,11367 L 12416,11373 L 12425,11380 L 12432,11388 L 12437,11397 L 12440,11407 L 12440,11418 L 12438,11429 L 12435,11441 L 12429,11454 L 12421,11467 L 12399,11495 L 12370,11525 L 12334,11556 L 12243,11624 L 12127,11695 L 11992,11768 L 11838,11840 L 11671,11910 L 11501,11972 L 11340,12023 L 11191,12061 L 11058,12087 L 10945,12100 L 10897,12101 L 10855,12099 L 10820,12093 L 10806,12089 L 10793,12083 L 10782,12077 L 10773,12070 L 10766,12062 L 10761,12053 z"/><path d="M 12410,11353 L 12408,11351 L 12406,11349 L 12404,11344 L 12402,11337 L 12402,11330 L 12402,11322 L 12403,11312 L 12409,11291 L 12418,11267 L 12430,11239 L 12465,11175 L 12511,11102 L 12568,11022 L 12635,10936 L 12710,10847 L 12788,10761 L 12864,10683 L 12937,10616 L 13003,10560 L 13061,10518 L 13087,10502 L 13110,10490 L 13130,10482 L 13139,10479 L 13147,10478 L 13154,10477 L 13161,10478 L 13166,10480 L 13169,10481 L 13171,10483 L 13173,10485 L 13175,10487 L 13177,10492 L 13179,10499 L 13180,10506 L 13179,10514 L 13178,10524 L 13173,10545 L 13164,10569 L 13152,10597 L 13117,10661 L 13071,10734 L 13014,10814 L 12947,10900 L 12872,10989 L 12794,11075 L 12718,11153 L 12645,11220 L 12579,11276 L 12521,11318 L 12495,11334 L 12472,11346 L 12451,11354 L 12442,11357 L 12434,11358 L 12427,11359 L 12420,11358 L 12415,11356 L 12412,11355 L 12410,11353 z"/><path d="M 8102,11826 L 8102,11791 L 8101,11755 L 8100,11720 L 8098,11685 L 8096,11651 L 8093,11617 L 8089,11583 L 8086,11550 L 8081,11518 L 8077,11487 L 8072,11456 L 8066,11427 L 8060,11398 L 8054,11371 L 8047,11344 L 8039,11319 L 8032,11296 L 8024,11273 L 8016,11252 L 8007,11233 L 7999,11215 L 7990,11198 L 7980,11184 L 7971,11171 L 7961,11159 L 7951,11149 L 7941,11141 L 7931,11135 L 7921,11131 L 7911,11128 L 7901,11127 L 7901,11127 L 7891,11128 L 7881,11131 L 7871,11135 L 7861,11141 L 7851,11149 L 7841,11159 L 7831,11171 L 7822,11184 L 7812,11198 L 7803,11215 L 7795,11233 L 7786,11252 L 7778,11273 L 7770,11296 L 7763,11319 L 7755,11344 L 7748,11371 L 7742,11398 L 7736,11427 L 7730,11456 L 7725,11487 L 7721,11518 L 7716,11550 L 7713,11583 L 7709,11617 L 7706,11651 L 7704,11685 L 7702,11720 L 7701,11755 L 7700,11791 L 7700,11826 L 7700,11826 L 7700,11861 L 7701,11897 L 7702,11932 L 7704,11967 L 7706,12001 L 7709,12035 L 7713,12069 L 7716,12102 L 7721,12134 L 7725,12165 L 7730,12196 L 7736,12225 L 7742,12254 L 7748,12281 L 7755,12308 L 7763,12333 L 7770,12356 L 7778,12379 L 7786,12400 L 7795,12419 L 7803,12437 L 7812,12454 L 7822,12468 L 7831,12481 L 7841,12493 L 7851,12503 L 7861,12511 L 7871,12517 L 7881,12521 L 7891,12524 L 7901,12525 L 7901,12525 L 7911,12524 L 7921,12521 L 7931,12517 L 7941,12511 L 7951,12503 L 7961,12493 L 7971,12481 L 7980,12468 L 7990,12454 L 7999,12437 L 8007,12419 L 8016,12400 L 8024,12379 L 8032,12356 L 8039,12333 L 8047,12308 L 8054,12281 L 8060,12254 L 8066,12225 L 8072,12196 L 8077,12165 L 8081,12134 L 8086,12102 L 8089,12069 L 8093,12035 L 8096,12001 L 8098,11967 L 8100,11932 L 8101,11897 L 8102,11861 L 8102,11826 z"/><path d="M 7825,12576 L 7819,12584 L 7810,12591 L 7801,12597 L 7789,12601 L 7777,12604 L 7762,12606 L 7730,12607 L 7692,12603 L 7649,12595 L 7602,12583 L 7551,12567 L 7438,12522 L 7313,12463 L 7181,12391 L 7043,12306 L 6910,12215 L 6790,12123 L 6685,12033 L 6599,11948 L 6563,11907 L 6533,11869 L 6508,11833 L 6490,11800 L 6477,11770 L 6473,11756 L 6471,11743 L 6470,11731 L 6471,11719 L 6474,11709 L 6479,11700 L 6486,11692 L 6494,11685 L 6504,11679 L 6515,11675 L 6528,11672 L 6542,11670 L 6575,11669 L 6613,11673 L 6656,11681 L 6703,11693 L 6754,11709 L 6867,11753 L 6992,11812 L 7124,11884 L 7262,11969 L 7395,12061 L 7515,12153 L 7619,12243 L 7706,12328 L 7741,12369 L 7771,12407 L 7796,12443 L 7815,12476 L 7827,12506 L 7831,12520 L 7834,12533 L 7834,12545 L 7833,12557 L 7830,12567 L 7825,12576 z"/><path d="M 6460,11695 L 6457,11697 L 6454,11699 L 6451,11701 L 6447,11702 L 6443,11703 L 6438,11703 L 6428,11702 L 6416,11700 L 6403,11696 L 6389,11691 L 6374,11684 L 6342,11666 L 6307,11643 L 6270,11616 L 6233,11584 L 6197,11550 L 6166,11517 L 6139,11485 L 6118,11455 L 6110,11441 L 6103,11427 L 6098,11415 L 6094,11404 L 6092,11393 L 6092,11389 L 6092,11385 L 6093,11381 L 6094,11377 L 6096,11374 L 6098,11371 L 6101,11369 L 6104,11366 L 6107,11365 L 6111,11364 L 6115,11363 L 6120,11363 L 6130,11363 L 6142,11366 L 6154,11370 L 6168,11375 L 6183,11382 L 6216,11399 L 6251,11422 L 6288,11450 L 6325,11481 L 6361,11515 L 6392,11548 L 6418,11581 L 6439,11611 L 6448,11625 L 6455,11638 L 6460,11651 L 6464,11662 L 6465,11672 L 6466,11677 L 6465,11681 L 6465,11685 L 6464,11689 L 6462,11692 L 6460,11695 z"/><path d="M 13184,10437 L 13182,10436 L 13179,10434 L 13175,10430 L 13171,10424 L 13168,10418 L 13166,10410 L 13164,10401 L 13163,10379 L 13164,10353 L 13167,10322 L 13179,10251 L 13200,10167 L 13229,10073 L 13266,9970 L 13309,9862 L 13357,9756 L 13405,9659 L 13453,9572 L 13498,9499 L 13540,9440 L 13560,9416 L 13578,9398 L 13595,9384 L 13602,9378 L 13610,9374 L 13616,9372 L 13623,9370 L 13629,9371 L 13631,9371 L 13634,9372 L 13636,9373 L 13639,9375 L 13643,9379 L 13646,9385 L 13649,9391 L 13651,9399 L 13653,9408 L 13655,9430 L 13654,9456 L 13651,9487 L 13638,9558 L 13617,9642 L 13588,9736 L 13551,9839 L 13508,9947 L 13461,10053 L 13413,10150 L 13365,10237 L 13320,10311 L 13278,10369 L 13258,10393 L 13240,10411 L 13223,10425 L 13216,10431 L 13208,10435 L 13202,10437 L 13195,10439 L 13189,10438 L 13187,10438 L 13184,10437 z"/><path d="M 10098,10825 L 10098,10790 L 10097,10754 L 10096,10719 L 10094,10684 L 10092,10650 L 10089,10616 L 10086,10582 L 10082,10549 L 10078,10517 L 10073,10486 L 10068,10455 L 10062,10426 L 10056,10397 L 10050,10370 L 10043,10343 L 10036,10318 L 10029,10295 L 10021,10272 L 10013,10251 L 10004,10232 L 9996,10214 L 9987,10197 L 9977,10183 L 9968,10170 L 9959,10158 L 9949,10148 L 9939,10140 L 9929,10134 L 9919,10130 L 9909,10127 L 9899,10126 L 9899,10126 L 9889,10127 L 9879,10130 L 9869,10134 L 9859,10140 L 9849,10148 L 9839,10158 L 9830,10170 L 9821,10183 L 9811,10197 L 9802,10214 L 9794,10232 L 9785,10251 L 9777,10272 L 9769,10295 L 9762,10318 L 9755,10343 L 9748,10370 L 9742,10397 L 9736,10426 L 9730,10455 L 9725,10486 L 9720,10517 L 9716,10549 L 9712,10582 L 9709,10616 L 9706,10650 L 9704,10684 L 9702,10719 L 9701,10754 L 9700,10790 L 9700,10825 L 9700,10825 L 9700,10860 L 9701,10896 L 9702,10931 L 9704,10966 L 9706,11000 L 9709,11034 L 9712,11068 L 9716,11101 L 9720,11133 L 9725,11164 L 9730,11195 L 9736,11224 L 9742,11253 L 9748,11280 L 9755,11307 L 9762,11332 L 9769,11355 L 9777,11378 L 9785,11399 L 9794,11418 L 9802,11436 L 9811,11453 L 9821,11467 L 9830,11480 L 9839,11492 L 9849,11502 L 9859,11510 L 9869,11516 L 9879,11520 L 9889,11523 L 9899,11524 L 9899,11524 L 9909,11523 L 9919,11520 L 9929,11516 L 9939,11510 L 9949,11502 L 9959,11492 L 9968,11480 L 9977,11467 L 9987,11453 L 9996,11436 L 10004,11418 L 10013,11399 L 10021,11378 L 10029,11355 L 10036,11332 L 10043,11307 L 10050,11280 L 10056,11253 L 10062,11224 L 10068,11195 L 10073,11164 L 10078,11133 L 10082,11101 L 10086,11068 L 10089,11034 L 10092,11000 L 10094,10966 L 10096,10931 L 10097,10896 L 10098,10860 L 10098,10825 z"/><path d="M 9827,11575 L 9821,11583 L 9812,11590 L 9803,11596 L 9791,11600 L 9779,11603 L 9764,11605 L 9732,11606 L 9694,11602 L 9651,11594 L 9604,11582 L 9553,11566 L 9440,11521 L 9315,11462 L 9183,11390 L 9045,11305 L 8912,11214 L 8792,11122 L 8687,11032 L 8601,10947 L 8565,10906 L 8535,10868 L 8510,10832 L 8492,10799 L 8479,10769 L 8475,10755 L 8473,10742 L 8472,10730 L 8473,10718 L 8476,10708 L 8481,10699 L 8488,10691 L 8496,10684 L 8506,10678 L 8517,10674 L 8530,10671 L 8544,10669 L 8577,10668 L 8615,10672 L 8658,10680 L 8705,10692 L 8756,10708 L 8869,10752 L 8994,10811 L 9126,10883 L 9264,10968 L 9397,11060 L 9517,11152 L 9621,11242 L 9708,11327 L 9743,11368 L 9773,11406 L 9798,11442 L 9817,11475 L 9829,11505 L 9833,11519 L 9836,11532 L 9836,11544 L 9835,11556 L 9832,11566 L 9827,11575 z"/><path d="M 6085,9230 L 6075,9220 L 6067,9209 L 6060,9197 L 6055,9184 L 6050,9169 L 6047,9154 L 6045,9120 L 6048,9083 L 6055,9042 L 6067,8998 L 6083,8952 L 6104,8904 L 6128,8853 L 6190,8749 L 6266,8641 L 6357,8533 L 6456,8432 L 6555,8346 L 6653,8274 L 6701,8245 L 6747,8220 L 6791,8199 L 6833,8183 L 6873,8172 L 6910,8165 L 6944,8164 L 6960,8166 L 6975,8169 L 6989,8173 L 7001,8179 L 7013,8186 L 7024,8194 L 7034,8204 L 7042,8215 L 7049,8227 L 7054,8241 L 7059,8255 L 7062,8271 L 7064,8304 L 7062,8342 L 7054,8383 L 7043,8426 L 7027,8473 L 7006,8521 L 6981,8571 L 6920,8676 L 6843,8784 L 6753,8892 L 6654,8993 L 6554,9079 L 6456,9151 L 6409,9180 L 6362,9205 L 6318,9226 L 6276,9242 L 6236,9253 L 6199,9259 L 6165,9260 L 6149,9259 L 6134,9256 L 6120,9251 L 6108,9246 L 6096,9239 L 6085,9230 z"/><path d="M 5910,9183 L 5900,9185 L 5890,9184 L 5879,9182 L 5868,9177 L 5856,9171 L 5845,9163 L 5820,9141 L 5795,9113 L 5769,9078 L 5743,9037 L 5716,8991 L 5663,8882 L 5611,8754 L 5561,8612 L 5516,8456 L 5479,8299 L 5451,8150 L 5434,8014 L 5427,7893 L 5427,7839 L 5430,7791 L 5435,7748 L 5443,7711 L 5454,7680 L 5460,7667 L 5467,7656 L 5474,7647 L 5483,7639 L 5491,7634 L 5501,7630 L 5511,7628 L 5521,7629 L 5532,7631 L 5543,7636 L 5555,7642 L 5566,7650 L 5591,7671 L 5616,7700 L 5642,7735 L 5668,7776 L 5695,7822 L 5748,7931 L 5800,8058 L 5850,8201 L 5895,8357 L 5932,8514 L 5960,8663 L 5977,8799 L 5984,8920 L 5984,8974 L 5981,9022 L 5975,9065 L 5967,9102 L 5957,9133 L 5951,9146 L 5944,9157 L 5936,9166 L 5928,9174 L 5919,9179 L 5910,9183 z"/><path d="M 8630,9344 L 8623,9336 L 8617,9328 L 8613,9318 L 8609,9306 L 8607,9294 L 8607,9281 L 8609,9251 L 8615,9217 L 8626,9180 L 8642,9140 L 8662,9097 L 8713,9004 L 8779,8903 L 8858,8798 L 8950,8691 L 9048,8589 L 9144,8500 L 9238,8425 L 9325,8365 L 9366,8341 L 9405,8321 L 9440,8306 L 9473,8296 L 9503,8291 L 9516,8291 L 9529,8291 L 9540,8294 L 9550,8297 L 9560,8302 L 9568,8308 L 9575,8316 L 9581,8325 L 9585,8335 L 9588,8346 L 9590,8358 L 9591,8372 L 9589,8401 L 9582,8435 L 9571,8472 L 9556,8512 L 9536,8555 L 9485,8648 L 9419,8749 L 9339,8854 L 9248,8961 L 9150,9063 L 9054,9152 L 8960,9228 L 8873,9288 L 8832,9312 L 8793,9331 L 8758,9346 L 8725,9356 L 8695,9361 L 8682,9362 L 8669,9361 L 8658,9359 L 8648,9355 L 8638,9350 L 8630,9344 z"/><path d="M 8566,9557 L 8557,9563 L 8547,9566 L 8536,9568 L 8524,9569 L 8511,9568 L 8497,9565 L 8465,9555 L 8431,9539 L 8393,9517 L 8353,9490 L 8310,9458 L 8218,9378 L 8120,9282 L 8019,9170 L 7917,9044 L 7821,8913 L 7739,8787 L 7670,8668 L 7617,8558 L 7597,8509 L 7581,8462 L 7569,8420 L 7562,8383 L 7561,8350 L 7562,8336 L 7564,8323 L 7567,8311 L 7572,8301 L 7578,8292 L 7586,8285 L 7595,8279 L 7605,8276 L 7616,8274 L 7628,8273 L 7641,8274 L 7655,8277 L 7687,8287 L 7721,8303 L 7759,8325 L 7799,8352 L 7842,8385 L 7934,8464 L 8032,8561 L 8133,8673 L 8235,8799 L 8330,8930 L 8413,9056 L 8482,9175 L 8535,9285 L 8555,9334 L 8571,9380 L 8583,9422 L 8589,9460 L 8591,9492 L 8590,9507 L 8588,9520 L 8585,9531 L 8580,9541 L 8574,9550 L 8566,9557 z"/><path d="M 6578,11626 L 6575,11627 L 6572,11627 L 6569,11627 L 6565,11626 L 6561,11624 L 6557,11622 L 6549,11616 L 6540,11608 L 6531,11598 L 6521,11586 L 6510,11573 L 6489,11541 L 6467,11503 L 6445,11460 L 6424,11413 L 6405,11365 L 6390,11319 L 6379,11277 L 6371,11239 L 6369,11222 L 6367,11207 L 6367,11193 L 6367,11181 L 6369,11171 L 6370,11166 L 6372,11163 L 6374,11159 L 6376,11157 L 6378,11155 L 6381,11153 L 6384,11152 L 6387,11152 L 6391,11152 L 6394,11153 L 6398,11155 L 6402,11157 L 6411,11163 L 6420,11171 L 6429,11181 L 6439,11193 L 6449,11206 L 6471,11238 L 6493,11276 L 6514,11319 L 6535,11366 L 6554,11414 L 6569,11460 L 6581,11502 L 6588,11540 L 6591,11557 L 6592,11572 L 6593,11586 L 6592,11598 L 6590,11608 L 6589,11613 L 6587,11616 L 6585,11620 L 6583,11622 L 6581,11624 L 6578,11626 z"/><path d="M 5952,11673 L 5953,11670 L 5955,11667 L 5957,11665 L 5960,11663 L 5963,11660 L 5967,11658 L 5977,11655 L 5989,11652 L 6003,11651 L 6037,11649 L 6077,11651 L 6122,11655 L 6172,11663 L 6224,11674 L 6276,11687 L 6323,11701 L 6366,11717 L 6402,11733 L 6432,11748 L 6444,11756 L 6454,11764 L 6461,11771 L 6464,11775 L 6466,11778 L 6468,11781 L 6469,11785 L 6469,11788 L 6469,11791 L 6468,11794 L 6466,11797 L 6464,11799 L 6461,11802 L 6458,11804 L 6454,11806 L 6444,11809 L 6432,11812 L 6418,11814 L 6384,11815 L 6344,11814 L 6299,11809 L 6249,11802 L 6197,11791 L 6145,11778 L 6098,11763 L 6055,11748 L 6019,11732 L 5989,11716 L 5977,11708 L 5967,11701 L 5960,11693 L 5957,11690 L 5955,11686 L 5953,11683 L 5952,11679 L 5952,11676 L 5952,11673 z"/><path d="M 5384,7616 L 5381,7618 L 5378,7620 L 5375,7622 L 5371,7623 L 5367,7624 L 5362,7624 L 5352,7623 L 5340,7621 L 5327,7617 L 5313,7612 L 5298,7605 L 5266,7587 L 5231,7564 L 5194,7537 L 5157,7505 L 5121,7471 L 5090,7438 L 5063,7406 L 5042,7376 L 5034,7362 L 5027,7348 L 5022,7336 L 5018,7325 L 5016,7314 L 5016,7310 L 5016,7306 L 5017,7302 L 5018,7298 L 5020,7295 L 5022,7292 L 5025,7290 L 5028,7287 L 5031,7286 L 5035,7285 L 5039,7284 L 5044,7284 L 5054,7284 L 5066,7287 L 5078,7291 L 5092,7296 L 5107,7303 L 5140,7320 L 5175,7343 L 5212,7371 L 5249,7402 L 5285,7436 L 5316,7469 L 5342,7502 L 5363,7532 L 5372,7546 L 5379,7559 L 5384,7572 L 5388,7583 L 5389,7593 L 5390,7598 L 5389,7602 L 5389,7606 L 5388,7610 L 5386,7613 L 5384,7616 z"/><path d="M 5502,7547 L 5499,7548 L 5496,7548 L 5493,7548 L 5489,7547 L 5485,7545 L 5481,7543 L 5473,7537 L 5464,7529 L 5455,7519 L 5445,7507 L 5434,7494 L 5413,7462 L 5391,7424 L 5369,7381 L 5348,7334 L 5329,7286 L 5314,7240 L 5303,7198 L 5295,7160 L 5293,7143 L 5291,7128 L 5291,7114 L 5291,7102 L 5293,7092 L 5294,7087 L 5296,7084 L 5298,7080 L 5300,7078 L 5302,7076 L 5305,7074 L 5308,7073 L 5311,7073 L 5315,7073 L 5318,7074 L 5322,7076 L 5326,7078 L 5335,7084 L 5344,7092 L 5353,7102 L 5363,7114 L 5373,7127 L 5395,7159 L 5417,7197 L 5438,7240 L 5459,7287 L 5478,7335 L 5493,7381 L 5505,7423 L 5512,7461 L 5515,7478 L 5516,7493 L 5517,7507 L 5516,7519 L 5514,7529 L 5513,7534 L 5511,7537 L 5509,7541 L 5507,7543 L 5505,7545 L 5502,7547 z"/><path d="M 4875,7594 L 4876,7591 L 4878,7588 L 4880,7586 L 4883,7584 L 4886,7581 L 4890,7579 L 4900,7576 L 4912,7573 L 4926,7572 L 4960,7570 L 5000,7572 L 5045,7576 L 5095,7584 L 5147,7594 L 5199,7607 L 5246,7622 L 5289,7637 L 5325,7653 L 5355,7669 L 5367,7677 L 5377,7684 L 5384,7692 L 5387,7695 L 5389,7699 L 5391,7702 L 5392,7706 L 5392,7709 L 5392,7712 L 5391,7715 L 5389,7718 L 5387,7720 L 5384,7722 L 5381,7725 L 5377,7727 L 5367,7730 L 5355,7733 L 5341,7734 L 5307,7736 L 5267,7734 L 5222,7730 L 5172,7722 L 5120,7711 L 5068,7698 L 5021,7684 L 4978,7668 L 4942,7653 L 4912,7637 L 4900,7629 L 4890,7621 L 4883,7614 L 4880,7610 L 4878,7607 L 4876,7604 L 4875,7600 L 4875,7597 L 4875,7594 z"/><path d="M 9763,8248 L 9761,8245 L 9759,8242 L 9758,8238 L 9758,8234 L 9757,8230 L 9758,8226 L 9759,8215 L 9763,8204 L 9768,8192 L 9775,8178 L 9784,8164 L 9805,8134 L 9831,8102 L 9862,8069 L 9897,8035 L 9934,8004 L 9970,7976 L 10005,7953 L 10037,7936 L 10052,7929 L 10066,7923 L 10079,7919 L 10090,7917 L 10100,7916 L 10105,7916 L 10109,7917 L 10113,7918 L 10116,7920 L 10119,7922 L 10122,7924 L 10124,7927 L 10126,7930 L 10127,7933 L 10127,7937 L 10128,7941 L 10127,7946 L 10126,7956 L 10122,7967 L 10117,7979 L 10110,7993 L 10102,8007 L 10080,8037 L 10054,8069 L 10023,8102 L 9988,8136 L 9951,8167 L 9914,8195 L 9880,8218 L 9847,8236 L 9833,8242 L 9819,8248 L 9806,8252 L 9794,8254 L 9784,8255 L 9780,8255 L 9775,8255 L 9772,8254 L 9768,8252 L 9765,8250 L 9763,8248 z"/><path d="M 9639,8179 L 9636,8177 L 9634,8175 L 9632,8173 L 9630,8169 L 9628,8166 L 9627,8161 L 9625,8151 L 9624,8139 L 9625,8126 L 9626,8110 L 9629,8093 L 9636,8056 L 9648,8013 L 9663,7968 L 9682,7920 L 9703,7873 L 9725,7830 L 9747,7792 L 9768,7760 L 9779,7746 L 9789,7734 L 9798,7724 L 9807,7717 L 9815,7711 L 9819,7709 L 9823,7707 L 9827,7706 L 9830,7706 L 9833,7706 L 9836,7707 L 9839,7708 L 9841,7710 L 9843,7713 L 9845,7716 L 9847,7720 L 9848,7724 L 9850,7734 L 9851,7746 L 9850,7760 L 9849,7775 L 9846,7792 L 9839,7830 L 9827,7872 L 9812,7918 L 9793,7966 L 9772,8013 L 9750,8056 L 9728,8094 L 9707,8126 L 9697,8140 L 9687,8152 L 9677,8162 L 9668,8169 L 9660,8175 L 9656,8177 L 9652,8179 L 9648,8180 L 9645,8180 L 9642,8180 L 9639,8179 z"/><path d="M 10269,8228 L 10269,8231 L 10269,8234 L 10268,8237 L 10267,8241 L 10264,8244 L 10262,8248 L 10254,8255 L 10244,8263 L 10232,8270 L 10203,8286 L 10166,8302 L 10123,8317 L 10076,8331 L 10024,8344 L 9972,8355 L 9922,8362 L 9877,8367 L 9837,8368 L 9803,8367 L 9789,8365 L 9777,8362 L 9767,8359 L 9763,8357 L 9760,8355 L 9757,8352 L 9755,8350 L 9753,8347 L 9752,8344 L 9752,8341 L 9752,8338 L 9753,8334 L 9755,8331 L 9757,8327 L 9760,8324 L 9767,8316 L 9777,8309 L 9789,8301 L 9803,8293 L 9819,8285 L 9855,8269 L 9898,8254 L 9946,8240 L 9998,8227 L 10050,8217 L 10099,8209 L 10144,8205 L 10184,8204 L 10218,8205 L 10232,8207 L 10244,8210 L 10254,8213 L 10258,8215 L 10261,8217 L 10264,8220 L 10266,8222 L 10268,8225 L 10269,8228 z"/><path d="M 9749,10085 L 9746,10087 L 9743,10089 L 9740,10091 L 9736,10092 L 9732,10093 L 9727,10093 L 9717,10092 L 9705,10090 L 9693,10086 L 9679,10081 L 9664,10074 L 9631,10056 L 9597,10034 L 9560,10006 L 9523,9975 L 9488,9941 L 9457,9908 L 9430,9876 L 9409,9846 L 9401,9832 L 9394,9819 L 9388,9806 L 9385,9795 L 9383,9785 L 9382,9780 L 9383,9776 L 9383,9772 L 9384,9768 L 9386,9765 L 9388,9762 L 9391,9760 L 9394,9758 L 9397,9756 L 9401,9755 L 9405,9754 L 9410,9754 L 9421,9755 L 9432,9757 L 9445,9761 L 9459,9767 L 9474,9774 L 9507,9791 L 9541,9814 L 9578,9841 L 9615,9872 L 9650,9906 L 9681,9939 L 9707,9971 L 9729,10001 L 9737,10015 L 9744,10029 L 9749,10041 L 9753,10052 L 9754,10063 L 9755,10067 L 9754,10071 L 9754,10075 L 9753,10079 L 9751,10082 L 9749,10085 z"/><path d="M 9868,10017 L 9865,10018 L 9862,10018 L 9859,10018 L 9855,10017 L 9852,10015 L 9848,10013 L 9840,10007 L 9831,9999 L 9821,9989 L 9801,9963 L 9780,9931 L 9758,9893 L 9736,9850 L 9715,9803 L 9696,9755 L 9681,9710 L 9670,9667 L 9662,9630 L 9658,9597 L 9657,9584 L 9658,9572 L 9660,9562 L 9661,9557 L 9662,9554 L 9664,9550 L 9666,9548 L 9668,9546 L 9671,9544 L 9674,9543 L 9677,9543 L 9680,9543 L 9684,9544 L 9688,9546 L 9692,9548 L 9700,9554 L 9709,9562 L 9718,9572 L 9728,9584 L 9739,9598 L 9760,9630 L 9782,9668 L 9803,9711 L 9824,9758 L 9843,9806 L 9858,9851 L 9870,9894 L 9878,9931 L 9880,9948 L 9882,9964 L 9882,9977 L 9882,9989 L 9880,9999 L 9879,10004 L 9877,10007 L 9875,10011 L 9873,10013 L 9871,10015 L 9868,10017 z"/><path d="M 9242,10063 L 9243,10060 L 9245,10057 L 9247,10055 L 9250,10053 L 9253,10050 L 9257,10048 L 9267,10045 L 9279,10042 L 9293,10041 L 9327,10039 L 9367,10041 L 9412,10045 L 9462,10053 L 9514,10064 L 9566,10077 L 9613,10091 L 9656,10107 L 9692,10123 L 9722,10138 L 9734,10146 L 9744,10154 L 9751,10161 L 9754,10165 L 9756,10168 L 9758,10171 L 9759,10175 L 9759,10178 L 9759,10181 L 9758,10184 L 9756,10187 L 9754,10189 L 9751,10192 L 9748,10194 L 9744,10196 L 9734,10199 L 9722,10202 L 9708,10204 L 9674,10205 L 9634,10204 L 9589,10199 L 9539,10192 L 9487,10181 L 9435,10168 L 9388,10153 L 9345,10138 L 9309,10122 L 9279,10106 L 9267,10098 L 9257,10091 L 9250,10083 L 9247,10080 L 9245,10076 L 9243,10073 L 9242,10069 L 9242,10066 L 9242,10063 z"/><path d="M 6841,4401 L 6832,4382 L 6827,4362 L 6826,4339 L 6828,4314 L 6834,4288 L 6843,4260 L 6872,4199 L 6914,4133 L 6968,4061 L 7035,3985 L 7112,3904 L 7298,3734 L 7521,3555 L 7777,3372 L 8060,3190 L 8352,3022 L 8632,2879 L 8894,2763 L 9130,2676 L 9237,2644 L 9336,2621 L 9424,2606 L 9503,2599 L 9570,2601 L 9599,2606 L 9625,2613 L 9648,2622 L 9667,2633 L 9683,2648 L 9696,2664 L 9705,2683 L 9710,2703 L 9711,2726 L 9709,2751 L 9703,2777 L 9694,2805 L 9665,2866 L 9623,2932 L 9569,3004 L 9502,3080 L 9425,3161 L 9239,3331 L 9016,3510 L 8760,3693 L 8477,3875 L 8185,4043 L 7905,4186 L 7643,4302 L 7407,4389 L 7300,4420 L 7201,4444 L 7113,4459 L 7034,4466 L 6967,4464 L 6938,4459 L 6912,4452 L 6889,4443 L 6870,4432 L 6854,4417 L 6841,4401 z"/><path d="M 9098,6041 L 9075,6069 L 9049,6094 L 9020,6117 L 8989,6137 L 8956,6155 L 8920,6170 L 8843,6193 L 8757,6206 L 8665,6209 L 8567,6203 L 8463,6188 L 8354,6163 L 8242,6129 L 8127,6087 L 8009,6035 L 7890,5975 L 7770,5907 L 7651,5830 L 7532,5745 L 7418,5654 L 7311,5560 L 7212,5464 L 7122,5366 L 7040,5267 L 6968,5168 L 6904,5069 L 6851,4972 L 6808,4876 L 6775,4783 L 6753,4694 L 6742,4608 L 6743,4527 L 6748,4489 L 6755,4452 L 6766,4417 L 6780,4383 L 6798,4351 L 6818,4321 L 6841,4293 L 6867,4268 L 6896,4245 L 6927,4225 L 6960,4207 L 6996,4192 L 7073,4169 L 7159,4156 L 7251,4152 L 7349,4158 L 7453,4174 L 7562,4199 L 7674,4232 L 7789,4275 L 7907,4326 L 8026,4387 L 8146,4455 L 8265,4532 L 8384,4617 L 8498,4708 L 8605,4802 L 8704,4898 L 8794,4996 L 8876,5095 L 8948,5194 L 9012,5293 L 9065,5390 L 9108,5486 L 9141,5579 L 9163,5668 L 9174,5754 L 9173,5835 L 9168,5873 L 9161,5910 L 9150,5945 L 9136,5979 L 9118,6011 L 9098,6041 z"/><path d="M 7879,5222 L 7868,5212 L 7858,5199 L 7851,5184 L 7845,5167 L 7841,5147 L 7839,5126 L 7841,5077 L 7850,5020 L 7866,4957 L 7888,4888 L 7917,4812 L 7992,4647 L 8091,4466 L 8210,4274 L 8348,4075 L 8496,3883 L 8643,3712 L 8786,3563 L 8921,3442 L 8984,3392 L 9044,3349 L 9099,3316 L 9150,3290 L 9196,3274 L 9217,3269 L 9237,3267 L 9255,3267 L 9272,3270 L 9287,3275 L 9300,3283 L 9311,3293 L 9321,3306 L 9329,3321 L 9334,3338 L 9338,3358 L 9340,3379 L 9338,3428 L 9329,3484 L 9314,3547 L 9291,3617 L 9263,3692 L 9187,3858 L 9089,4039 L 8970,4231 L 8832,4430 L 8684,4621 L 8536,4793 L 8393,4941 L 8258,5063 L 8195,5113 L 8136,5155 L 8080,5189 L 8029,5214 L 7983,5231 L 7962,5235 L 7942,5238 L 7924,5238 L 7907,5235 L 7892,5230 L 7879,5222 z"/><path d="M 9004,6100 L 8984,6094 L 8965,6084 L 8948,6069 L 8931,6050 L 8915,6027 L 8901,6001 L 8875,5936 L 8854,5857 L 8837,5764 L 8825,5660 L 8818,5543 L 8817,5279 L 8834,4980 L 8871,4652 L 8927,4303 L 8999,3957 L 9082,3638 L 9173,3352 L 9269,3106 L 9317,2999 L 9366,2906 L 9415,2825 L 9463,2759 L 9510,2708 L 9533,2688 L 9556,2672 L 9578,2661 L 9600,2653 L 9621,2651 L 9642,2652 L 9662,2658 L 9681,2668 L 9699,2683 L 9715,2702 L 9731,2725 L 9746,2752 L 9771,2816 L 9793,2895 L 9809,2988 L 9821,3093 L 9829,3209 L 9830,3473 L 9812,3773 L 9775,4100 L 9719,4449 L 9647,4795 L 9564,5114 L 9473,5400 L 9377,5647 L 9329,5753 L 9280,5846 L 9231,5927 L 9183,5993 L 9136,6044 L 9113,6064 L 9090,6080 L 9068,6091 L 9046,6099 L 9025,6101 L 9004,6100 z"/></g></g></svg> static/images/ok.svg<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width='32' height='32' viewBox='0 0 100 100'><circle cx='50' cy='50' r='40' fill='green'/></svg> static/images/bad.svg<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width='32' height='32' viewBox='0 0 100 100'><circle cx='50' cy='50' r='40' fill='red'/></svg> static/images/off.svg<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width='32' height='32' viewBox='0 0 100 100'><circle cx='50' cy='50' r='40' fill='black'/></svg> static/images/yellow.svg<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width='32' height='32' viewBox='0 0 100 100'><circle cx='50' cy='50' r='40' fill='yellow'/></svg> static/images/fork.svg<?xml version="1.0" encoding="utf-8"?> <!-- Origin: https://seekicon.com/free-icon/fork_4 2022-07-01 License: SIL (OFL) --> <!-- Generator: Adobe Illustrator 24.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve"><g><path d="M18,399.3h79.4V326c0-23.2,11.3-48.8,33-70.3C152,234.2,184.2,210.9,237,174c53.1-37.2,85.1-57.8,99.6-70.7 c14.4-12.9,13.2-10.8,13.2-29.6V0.9V0h63.1v0.9v72.7c0,28.2-11.8,56.6-34.2,76.6c-22.4,20-53.4,39-105.5,75.4 c-52.3,36.6-83.3,59.9-98.4,74.9c-15.1,15-14.3,15.6-14.3,25.5v73.4h82.8L130.7,512L18,399.3z M268.7,399.3h81.1V326 c0-9.8,0.8-10.5-14.3-25.5c-10-9.9-27.8-24-53.2-42.6c3.2-2.3,5.7-4.1,9.1-6.4c18.6-13,32.1-22,46-31.5 c17.7,13.4,31.5,24.8,42.5,35.7c21.7,21.4,33,47.1,33,70.3v73.4H494L381.4,512L268.7,399.3z"/></g></svg> |