Code Monkey home page Code Monkey logo

compscript's Introduction

CompScript

CompScript is a tool for assigning groups and staff assignments for WCA Rubik's Cube competitions. Other similar tools include Groupifier, AGE, and Delegate Dashboard. Those tools are intended to be user-friendly, automated systems that give you a few configuration options, let you click a button to generate all assignments, then allow for fine-tuning. This is a power-user tool -- it is designed for competitions where the organizer would consider spending multiple hours assigning groups, due to complicated constraints like multiple stages, dedicated staff, a live stream, and a desire to have a large amount of control over how groups should be assigned. Competitions with up to ~200 competitors and only one stage are likely served better by another tool.

CompScript is designed primarily for CubingUSA Nationals and other large championships. Requirements for other competitions may not be prioritized.

Running

Node must be installed on your machine.

$ npm install
$ npm run dev-server

Running the development server will use uses a dev WCA environment running on the same machine. If you would like to use the production WCA site, you need to:

  1. Make an OAuth application here. For "Scopes", use public manage_competitions; for "Callback Urls" use http://localhost:3033/auth/oauth_response.
  2. Make a copy of the .env.DEV file, such as .env.PROD. This file should not be committed; .gitignore should automatically ignore it.
  3. Replace WCA_HOST, API_KEY, and API_SECRET with the production values. You should also consider changing the COOKIE_SECRET to a new value, and to change PORT to 3033 to distinguish from the dev version.
  4. Run with $ ENV=PROD node main.js, using the file suffix you used above.

Scripts

You can enter scripts in the script box, using a custom language called CompScript. See docs/scripts.md for full documentation.

Some examples:

The Luke psych sheet

Table(
  Sort(Persons(And(Registered(), (FirstName() == "Luke"))),
       PersonalBest(_333, "average")),
  [Column("Name", Name()),
   Column("WCA ID", WcaId(), WcaLink()),
   Column("Average", PersonalBest(_333)),
   Column("Single", PersonalBest(_333, "single")),
   Column("psych sheet ranking", PsychSheetPosition(_333))])

Defining a custom function

Define(
  "SumOfRankings",
  (PsychSheetPosition({1, Event}, "average") + PsychSheetPosition({1, Event}, "single")))

which can then be called by:

SumOfRankings(_333)

Google Sheets integration

We use the google-spreadsheets NPM package to read from Google Sheets. Please refer to this page for how to create a Service Account with Google Sheets access. Move the generated JSON file to google-credentials.json in the top-level project directory, and make sure to grant the service account access to the spreadsheet you'd like it to read.

Do not share the service account credentials with anyone who should not have access to the spreadsheets you'd like to read.

compscript's People

Contributors

timreyn avatar viroulep avatar jfly avatar dependabot[bot] avatar

Stargazers

Alexandre Ondet avatar  avatar  avatar Sarah Strong avatar Simon Kelly avatar Cailyn avatar

Watchers

 avatar  avatar

Forkers

jfly viroulep

compscript's Issues

Printouts

Various printouts to distribute during the competition:

  • per-person schedule sheets (sorted by staff team, fixed height)
  • Master sheet for stage leads

Function arg nullability

There are going to be lots of null checks, most of which are just going to be early returns.

Add a nullable field to args. If not set to true, pass the null upward. If true, the function should handle it differently.

Rework JobCountScorer

JobCountScorer prefers giving staff jobs to people who have done less work so far. There are a few issues:

  • Not all jobs are the same -- a 2x2 group that lasts 10 minutes should be weighted less than judging Multi for an hour
  • Some credit should be given to people competing, not just staffing. While ideally everyone would get the same amount of work, regardless of how many events they do, in practice we don't want to burn out staff who have a lot of events. Competing should probably count for approximately 1/4 the weight of judging, but this can be decided by the calling script
  • If we are considering competing, we should only consider jobs that have already happened. Otherwise, if groups are assigned first, and then judging, then people doing a lot of events would have no jobs at the beginning of the competition, then switch to a full workload.

Scorecards

Figure out how to do scorecards. Fork groupifier scorecard code?

Fix ExtractOne

Rather than a regex, it should use a grammar to make sure it extracts the right number of angle braces.

e.g. right now if expectedValue is Array<Tuple<Number, String>> then genericValue is Tuple<Number, String>>.

Staff assignments

Make a linear programming model for staff assignments on each group for a given round.

Empty arrays

Currently passing [] as an argument makes it an Array<$T>, which fails to resolve in whatever calling function expects an Array. Figure out a way to make this work.

Add tests

Compscript is complicated enough that it would probably be worthwhile to add unit tests.

Consider typecasting

There are a limited number of cases where it would be helpful to implicitly cast, e.g. Group -> Round, Round -> Event.

Be careful with extending this too far, e.g. Number -> String could break Add().

Add external args

It's weird to have e.g. a function that returns Boolean(Person) and takes an Activity as an arg, and an equivalent Boolean(Activity) that takes Person as an arg.

Refactor arg matching + selection in driver.js to allow some function args to be external (i.e. provided when resolving, not in the textbox).

Allow specifying generic types

Type inference is certainly not perfect. Consider allowing the input to specify types, e.g.

Function<TypeA, TypeB>(args)

Break Event, Round, and Group into separate types

The parser should recognize Event and Round differently. Event and Round can continue to be represented by an ActivityCode.

The parser does not need to recognize Group; this should be an internal-only type. The underlying representation should be a WCIF Activity. ActivityCodes for Groups are ambiguous after #17, so ActivityCode is no longer sufficient.

There should be conversion methods between these three types.

What is the type of cluster's output?

  • It's stored as a string in wcif, apparently
  • (NumberProperty("staff-team") == NumberProperty("staff-team", 2008CLEM01)) works
  • (StringProperty("staff-team") == "1") doesn't work
  • (NumberProperty("staff-team") == 1) doesn't work

??

Document usage of ReadSpreadsheet

At the moment this is more a question for which the answer should likely be public: is there any basic example out there of a basic spreadsheet format and the result it provides in person's properties when imported?

I've been shared some private example usage of compscript, but the data in spreadsheets have been appropriately omitted, and now I'm left with a few questions on the format I should target (because importing data dynamically sounds like a great idea for Euros ;)).

Let's take for instance the following data (csv, but separated by ';' intentionally, because of how I think values are parsed in the code):

identifier:string:wca_id;identifier:integer:user_id;property:list:scramble-events;property:string:team-kind;ignore:Some nice thing
2008VIRO01;;3x3,2x2;teams;titi
2008PIAU01;;5x5,2x2;teams;toto
;61698;;teams;tutu
2018GODI01;;;data-entry;tata

Reading from the source code, I would guess this results in adding the properties "scramble-events" and "team-kind", respectively holding an array of string and a string, that I would be able to query using respectively ArrayProperty("scramble-events") and StringProperty("team-kind")?
The "Some nice thing" column would simply be ignored.

Could you please confirm using this format is enough to identify whom to attach the data to based on the wca_id/user_id fields? (and does it matter if both of them are filled?)

What happens if one of the columns' header doesn't have the ignore:/identifier:/property: prefix?

For the record I still intend to run a test competition with a simplified example of basically everything, that could serve as a publicly available showcase of compscript (and for which I could make the spreadsheet used public, hence "fixing" this "issue".

Switch to numerical groups

Currently group names are of the form "Red1". This will break

Other tools like competitiongroups.com display the room name (Red) next to the group number, so this isn't necessary. It also will break code like Groupifier's parseActivityCode.

Switch to assuming group names are numbers.

Update the table example

This is the table in the readme:

Table(
  Persons(And(Registered(), (FirstName() == "Luke"))),
  [Column("Name", Name()),
   Column("WCA ID", WcaId(), WcaLink()),
   Column("Average", PersonalBest(_333)),
   Column("Single", PersonalBest(_333, "single")),
   Column("psych sheet ranking", PsychSheetPosition(_333))],
  PersonalBest(_333, "average"))

It does not seem to work "as-is", and I think it's because of recent changes in the sort functions.

Removing the last arguments makes it work, but loses the sort capabilities: would you have an updated example to sort based on an attribute of the elements?

Reduce amount of logging/make it optional

I've started playing a bit more on real data: creating and assigning groups emits a lot of output; just providing a sample below:

Added group 7x7x7 Cube Round 1 Blue 1 from 2024-07-25T10:00:00.000+02:00 to 2024-07-25T10:35:00.000+02:00
Added group 7x7x7 Cube Round 1 Blue 2 from 2024-07-25T10:35:00.000+02:00 to 2024-07-25T11:10:00.000+02:00
Added group 7x7x7 Cube Round 1 Red 1 from 2024-07-25T10:00:00.000+02:00 to 2024-07-25T10:35:00.000+02:00
Added group 7x7x7 Cube Round 1 Red 2 from 2024-07-25T10:35:00.000+02:00 to 2024-07-25T11:10:00.000+02:00
...
3x3x3 Fewest Moves Round 1 Groups
Side 1 (368)
... followed by the table of the group

It's absolutely great to have that output, but the concatenation of everything makes it a bit hard to read.
It would be awesome to be able to "opt in" or "opt out" of these outputs (ideally I'd love to select a couple of interesting outputs, but not everything).

Is there an option/parameter I missed?
Would you be open to have a way to "silence" the output for some calls?

Improvements to staff assignments

-Not enough breaks before competing -- increase the weight of this scorer or reset?
-Not enough anchoring to prior group job -- maybe change the way this works, to decay based on the number of previous consecutive groups (basically try to avoid singleton jobs)

Overwrite groupifier extensions

Editing groupifier extensions on groupifier (e.g. page size, scorecard layout) seems to require setting a number of groups. I'm not sure what side effects that will have. Add a way to write the extensions we need directly from natshelper.

The function AddPerson doesn't fill person's details

I think it's intended, but I'll create an issue just in case: AddPerson only add the wcaUserId to the person's detail in the WCIF.
I read a bit the activity on the WCA website repository related to non competing staff, and it looks like to me that the intended workflow is the following:

  • compscript adds the person's wcaUserId to the WCIF
  • executing the change on the WCA website makes it create a registration for that user
  • upon subsequent execution in compscript, the person's details actually show up now that the registration exists

Am I correct in how that function is supposed to be used?

I think there are a few actions that can be taken:

  • document the function's behavior, and mention that forgetting to push to the WCA website will lead to incomplete information about persons, and crashes in some cases.
  • optionally compscript could query the WCA website in such cases, to include the user's data right away in the WCIF (which should be fine even if we got them wrong, because they would be erased anyway when the website creates the actual registration).
  • if we don't to query the website for that, we may also include dummy data until the registration is properly create (eg "Imported Person XYZ" with a fake dob); just so that the rest of the script doesn't crash when in "dry-run" mode.

I can definitely take care of 1, but what do you think about doing 2?
If you think it's a corner case I'd definitely be willing to do 3, as I do a lot of "dry-run" things before actually patching the WCIF!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.