Initial commit
This commit is contained in:
commit
10004a555e
5 changed files with 796 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
testdata/
|
394
aliasparts.csv
Normal file
394
aliasparts.csv
Normal file
|
@ -0,0 +1,394 @@
|
|||
first,second
|
||||
Balls,Bear
|
||||
Fluffy,Flight
|
||||
Fashionable,Handbag
|
||||
Vengeful,Direction
|
||||
Embarrass,Example
|
||||
Night,Attention
|
||||
Redundant,Bag
|
||||
Invention,Invention
|
||||
Modular,Wolf
|
||||
Closet,Oyster
|
||||
Pleasant,Pancake
|
||||
Study,Sun
|
||||
Ravioli,Result
|
||||
Boats,Power
|
||||
Feeling,Donkey
|
||||
Watery,Water
|
||||
Construct,Scarecrow
|
||||
Penitent,Journey
|
||||
Ripe,Brick
|
||||
Basketball,Boys
|
||||
Petite,Pickle
|
||||
Delicious,Carriage
|
||||
Jimmy,Jamaica
|
||||
Lively,Army
|
||||
Sulky,Visitor
|
||||
Tricky,Time
|
||||
Incompetent,Flight
|
||||
Telephone,Tub
|
||||
Necessary,Skirt
|
||||
Penguin,Printer
|
||||
Mad,Rear
|
||||
Gamer,Breath
|
||||
Teeny-Tiny,Toe
|
||||
Attempt,Dinosaurs
|
||||
Sudden,Money
|
||||
Shoot,Side
|
||||
Zesty,Horse
|
||||
Annoying,Caption
|
||||
Paprika,Bane
|
||||
Team,Wisdom
|
||||
Offend,Twists
|
||||
Evening,Supreme
|
||||
Inca,Kola
|
||||
Omega,Society
|
||||
Ladybug,Zoomies
|
||||
Offbeat,Grass
|
||||
Charlie,Vibrate
|
||||
Cheese,Cannon
|
||||
Bad,Timing
|
||||
End,Pancake
|
||||
Climax,Koalas
|
||||
Solo,Gopher
|
||||
Courageous,Curtain
|
||||
Join,Battle
|
||||
Disagreeable,Behavior
|
||||
Use,Floor
|
||||
False,Opinion
|
||||
Chunky,Curtain
|
||||
Somber,Bells
|
||||
Painful,Lawyer
|
||||
Round,Sphere
|
||||
Frightening,Credit
|
||||
Best,Grandfather
|
||||
Axiom,Address
|
||||
False,Oil
|
||||
Soothing,Saxophone
|
||||
Ugly,Lobby
|
||||
Whimsical,Advertisement
|
||||
Marvelous,Toes
|
||||
Mysterious,Beast
|
||||
Sulfur,Sphere
|
||||
Attempt,Nut
|
||||
Responsible,Floor
|
||||
Looping,Jungle
|
||||
Lethal,Geese
|
||||
Legal,Legs
|
||||
Mere,Egg
|
||||
Stocking,Slave
|
||||
General,Giraffe
|
||||
Simon,Switch
|
||||
Purple,Pancake
|
||||
Nice,Distribution
|
||||
Flesh,Toothbrush
|
||||
Political,Argument
|
||||
Sweet,Creator
|
||||
Scatter,Boys
|
||||
Collect,Glue
|
||||
Salami,Society
|
||||
Alcoholic,Goat
|
||||
Swear,Police
|
||||
Wear,Banana
|
||||
Display,Smash
|
||||
Volcano,Frank
|
||||
Temple,Hippie
|
||||
Obtain,Loss
|
||||
Two,Cats
|
||||
Nice,Spoon
|
||||
Marvelous,Competition
|
||||
Long,Lips
|
||||
Ostentatious,Ocelot
|
||||
Religion,Gun
|
||||
Pedro,Pablo
|
||||
Operation,Cobweb
|
||||
Dad,Bambino
|
||||
Cuba,Colombo
|
||||
Pot,Island
|
||||
Jungle,Jazz
|
||||
Strip,Disappointment
|
||||
Irritating,Idea
|
||||
Psychotic,Frog
|
||||
Fearful,Form
|
||||
Vast,Error
|
||||
Accurate,Mine
|
||||
Fretful,Form
|
||||
Nice,Name
|
||||
Fascinated,Father
|
||||
Consider,Hate
|
||||
Help,Help
|
||||
Climax,Outside
|
||||
Ordinary,Thread
|
||||
Enormous,Icicle
|
||||
Dark,Development
|
||||
Enigma,Emerald
|
||||
Test,Time
|
||||
Geese,Goose
|
||||
Functional,Cemetery
|
||||
Obscene,Thing
|
||||
Prefer,Pie
|
||||
Public,Jellyfish
|
||||
Grumpy,Ghost
|
||||
Palma,Yes
|
||||
Society,Shelter
|
||||
Tasteful,Turn
|
||||
Accelerate,Breakfast
|
||||
Blind,Jail
|
||||
Scream,Connection
|
||||
Zodiac,Zipper
|
||||
Disturbed,Man
|
||||
Orange,Matrix
|
||||
Pain,Party
|
||||
Mere,Monkey
|
||||
Aggressive,Agreement
|
||||
Name,Son
|
||||
Propose,Shoes
|
||||
Lewd,Cake
|
||||
Hard-To-Find,Stream
|
||||
Suspend,Sleep
|
||||
Magic,Mike
|
||||
Sound,Wave
|
||||
Song,Credit
|
||||
Numberless,Design
|
||||
Play,Segment
|
||||
Moldy,Maid
|
||||
Omega,Oregano
|
||||
Flimsy,Balls
|
||||
Become,Bat
|
||||
Neuron,Magenta
|
||||
Hum,Balls
|
||||
Shaggy,Destruction
|
||||
Big,Soup
|
||||
Noisy,Machine
|
||||
Convert,Grandmother
|
||||
Aspiring,Floor
|
||||
Legs,Help
|
||||
Alien,Agenda
|
||||
Exceed,Crack
|
||||
Nominate,Nut
|
||||
Stupendous,Brain
|
||||
Distant,Tomato
|
||||
Bad,Bells
|
||||
Stink,Side
|
||||
Athletic,Snorlax
|
||||
Economy,Expert
|
||||
Dynamic,Design
|
||||
David,Gilbert
|
||||
Orange,Matrix
|
||||
Pain,Party
|
||||
Mere,Monkey
|
||||
Aggressive,Agreement
|
||||
Name,Son
|
||||
Propose,Shoes
|
||||
Lewd,Cake
|
||||
Hard-To-Find,Stream
|
||||
Suspend,Sleep
|
||||
Magic,Mike
|
||||
Sound,Wave
|
||||
Song,Credit
|
||||
Numberless,Design
|
||||
Play,Segment
|
||||
Moldy,Maid
|
||||
Omega,Oregano
|
||||
Flimsy,Balls
|
||||
Become,Bat
|
||||
Neuron,Magenta
|
||||
Hum,Balls
|
||||
Shaggy,Destruction
|
||||
Big,Soup
|
||||
Noisy,Machine
|
||||
Convert,Grandmother
|
||||
Aspiring,Floor
|
||||
Legs,Help
|
||||
Alien,Agenda
|
||||
Exceed,Crack
|
||||
Nominate,Nut
|
||||
Stupendous,Brain
|
||||
Distant,Tomato
|
||||
Bad,Bells
|
||||
Stink,Side
|
||||
Athletic,Snorlax
|
||||
Economy,Expert
|
||||
Dynamic,Design
|
||||
David,Gilbert
|
||||
Teal,Pocket
|
||||
Troubleshoot,Things
|
||||
Voice,Offset
|
||||
Paint,Everywhere
|
||||
Wakeful,Train
|
||||
Not,Puppy
|
||||
Disapprove,Year
|
||||
Uncle,Worm
|
||||
Bored,Brother
|
||||
Panoramic,Visitor
|
||||
Feeble,Fruit
|
||||
Fantastic,Shop
|
||||
Swing,Swung
|
||||
Angry,Father
|
||||
Tremendous,Expansion
|
||||
Try,Wing
|
||||
Punishment,Achiever
|
||||
Drayze,Money
|
||||
Plague,Doctor
|
||||
Calculator,Enter
|
||||
Barrel,Replica
|
||||
Wiggly,Boy
|
||||
Complex,Crook
|
||||
Mimosa,Monarch
|
||||
Graffiti,Soul
|
||||
Ghost,Flower
|
||||
Slow,Sonic
|
||||
Bite,Committee
|
||||
Papa,Puma
|
||||
Holistic,Tooth
|
||||
Dramatic,Man
|
||||
Grouchy,Grape
|
||||
Juicy,Jazz
|
||||
Spiky,Chimp
|
||||
Expect,Pain
|
||||
Cheese,Meteor
|
||||
Two,Swords
|
||||
Psychedelic,Use
|
||||
Shiver,Sleet
|
||||
Foresee,Foot
|
||||
Sonic,Phantom
|
||||
Pam,Can
|
||||
Inadvisable,Machine
|
||||
Game,Cloth
|
||||
Smiling,Mother
|
||||
Finger,Sized
|
||||
Abhorrent,Aftermath
|
||||
Pancake,Maiden
|
||||
Sentient,Jalapeño
|
||||
Snapple,Sider
|
||||
Employ,Ray
|
||||
Agenda,Dandy
|
||||
Punctual,Pennies
|
||||
Child,Cannon
|
||||
Jetstream,Sam
|
||||
Mana,Vein
|
||||
Smite,Sword
|
||||
Diva,Dee
|
||||
Silly,Savage
|
||||
Genre,Damage
|
||||
Concussion,Simulator
|
||||
Chew,Beef
|
||||
Certainly,Hi-Tech
|
||||
Born,Loser
|
||||
Prefers,Broccoli
|
||||
Spooked,Broccoli
|
||||
Devilish,Afterthought
|
||||
Chaos,Dunk
|
||||
Phobic,Babies
|
||||
Hammer,Lunchroom
|
||||
Demonic,Holds
|
||||
Expo,Jinx
|
||||
Empowered,Turtle
|
||||
Amish,Shinobi
|
||||
More,Thirteens
|
||||
Disprove,Woman
|
||||
Young,Thug
|
||||
Angry,Crayon
|
||||
Examine,Harmony
|
||||
Vivacious,Underwear
|
||||
Operate,Swing
|
||||
Continue,Frolicking
|
||||
Nutritious,Pot
|
||||
Song,Lot
|
||||
Love,Lift
|
||||
Saucy,Style
|
||||
Read,Rules
|
||||
Steppable,Horror
|
||||
Large,Tune
|
||||
Trans,Rights
|
||||
Flamenco,Hardstyle
|
||||
Huge,Jazz
|
||||
Tech,Alert
|
||||
Maybe,Eleven
|
||||
Inform,Death
|
||||
Shapeshifting,Soul
|
||||
Splashy,Splash
|
||||
Dimo,Pandering
|
||||
Snooping,Canine
|
||||
Tech,Notations
|
||||
Two,Words
|
||||
Turnt,Tables
|
||||
Micro,Mambo
|
||||
Forgetful,Form
|
||||
Emotion,Lotion
|
||||
Nine,Ten
|
||||
The,The
|
||||
Sodium,Carbonate
|
||||
Colorful,Chase
|
||||
Weed,Steps
|
||||
Very,Serious
|
||||
Improved,Nut
|
||||
Ooh,Ahh
|
||||
Fasten,Spoon
|
||||
Substandard,Senior
|
||||
Dispose,Cigarette
|
||||
Ghoughpteighbteau,Tchoghs
|
||||
Sixty,Nine
|
||||
Seventeen,Sixteenths
|
||||
Cool,Ten
|
||||
Soft,Pants
|
||||
Stylish,Seagull
|
||||
Foot,Prints
|
||||
Bee,Sat
|
||||
New,Fifteen
|
||||
Sandwiched,Romance
|
||||
Makita,Power
|
||||
No,Fantastics?
|
||||
Jellyfish,Jambalaya
|
||||
None,Modifiers
|
||||
Redacted,Redacted
|
||||
Butter,Fly
|
||||
Fray,Yeah
|
||||
Naur,Non
|
||||
Go-Go,Ghost
|
||||
Not,Eleven
|
||||
Disk,Doctor
|
||||
Hop,Along
|
||||
Last,Literature
|
||||
Corrupt,File
|
||||
Dazzlin,Darlin
|
||||
Leaping,Lemons
|
||||
Boys,Off
|
||||
Egglord,Turds
|
||||
Everybody's,Something
|
||||
Chief,Poser
|
||||
Get,Funked
|
||||
Rhythm,Challenge
|
||||
Easy,Breezy
|
||||
Diabolic,Dingleberry
|
||||
Tucan,Bounce
|
||||
Quatre,Cinq
|
||||
Vanity,Devil
|
||||
Alice,Liddell
|
||||
Friendly,Frog
|
||||
Secret,Santa
|
||||
Moonlight,Silver
|
||||
Imperfect,Title
|
||||
Chicken,Nugget
|
||||
Shelf,Elf
|
||||
Eccentric,Catastrophe
|
||||
Beaver,Bunions
|
||||
Dig,Drug
|
||||
Elegant,Eggnog
|
||||
Uncharacteristically,Sesquipedalian
|
||||
Indecisive,Identity
|
||||
Play,Melty
|
||||
Tranquil,Rat
|
||||
Hall,Days
|
||||
Unexpected,Item
|
||||
An,Chart
|
||||
Snail,Spoon
|
||||
Boss,Fight
|
||||
Got,Spunned
|
||||
Throat,Goat
|
||||
Scientific,Icicle
|
||||
Grateful,Knot
|
||||
Sun,Goddess
|
||||
Banana,Special
|
||||
Sleigh,It
|
||||
Impossible,Burst
|
|
177
anonymize-entries.py
Normal file
177
anonymize-entries.py
Normal file
|
@ -0,0 +1,177 @@
|
|||
import argparse
|
||||
import csv
|
||||
import _csv
|
||||
import os
|
||||
from random import Random
|
||||
import sys
|
||||
from tempfile import TemporaryDirectory
|
||||
from typing import Optional
|
||||
from zipfile import ZipFile
|
||||
|
||||
from fs.zipfs import ZipFS
|
||||
import simfile
|
||||
|
||||
|
||||
####################
|
||||
# Script arguments #
|
||||
####################
|
||||
|
||||
|
||||
class AnonymizeEntriesArgs:
|
||||
"""Stores the command-line arguments for this script."""
|
||||
|
||||
csv: str
|
||||
files: str
|
||||
dry_run: bool
|
||||
|
||||
|
||||
def argparser():
|
||||
"""Get an ArgumentParser instance for this command-line script."""
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("csv", type=str, help="path to the CSV file of form responses")
|
||||
parser.add_argument(
|
||||
"files", type=str, help="path to the directory of file responses"
|
||||
)
|
||||
parser.add_argument(
|
||||
"-d",
|
||||
"--dry-run",
|
||||
action=argparse.BooleanOptionalAction,
|
||||
help="preview changes without writing the file",
|
||||
)
|
||||
return parser
|
||||
|
||||
|
||||
CsvContents = list[dict[str, str]]
|
||||
|
||||
|
||||
################
|
||||
# Script logic #
|
||||
################
|
||||
|
||||
|
||||
def assert_valid_file_paths(args: AnonymizeEntriesArgs):
|
||||
assert os.path.isfile(args.csv), f"{repr(args.csv)} is not a file"
|
||||
assert os.path.isdir(args.files), f"{repr(args.files)} is not a directory"
|
||||
|
||||
|
||||
def load_csv_contents(args: AnonymizeEntriesArgs):
|
||||
with open(args.csv, "r") as csvfile:
|
||||
return list(csv.DictReader(csvfile))
|
||||
|
||||
|
||||
def load_alias_parts(csvpath: str) -> tuple[list[str], list[str]]:
|
||||
def extract_alias_parts(csv: "_csv._reader"):
|
||||
return tuple(zip(*((line[0], line[1]) for n, line in enumerate(csv) if n > 0)))
|
||||
|
||||
with open(csvpath, "r") as csvfile:
|
||||
alias_parts = extract_alias_parts(csv.reader(csvfile))
|
||||
|
||||
print(f"Loaded {sum(len(part) for part in alias_parts)} alias parts")
|
||||
|
||||
return alias_parts
|
||||
|
||||
|
||||
def assert_known_google_forms_columns_present(csv_contents: CsvContents):
|
||||
assert (
|
||||
"Timestamp" in csv_contents[0]
|
||||
), 'Provided CSV file does not have a "Timestamp" column'
|
||||
assert (
|
||||
"Email Address" in csv_contents[0]
|
||||
), 'Provided CSV file does not have an "Email Address" column'
|
||||
|
||||
|
||||
def detect_filename_column(csv_contents: CsvContents) -> str:
|
||||
maybe_filename_columns = [
|
||||
column for (column, value) in csv_contents[0].items() if value.endswith(".zip")
|
||||
]
|
||||
assert (
|
||||
len(maybe_filename_columns) != 0
|
||||
), 'First data row of provided CSV file has no cell ending in ".zip"'
|
||||
assert (
|
||||
len(maybe_filename_columns) == 1
|
||||
), 'First data row of provided CSV file has multiple cells ending in ".zip"'
|
||||
filename_column = maybe_filename_columns[0]
|
||||
print(f"Detected filename column: {repr(filename_column)}")
|
||||
return filename_column
|
||||
|
||||
|
||||
def maybe_generate_and_persist_aliases(
|
||||
args: AnonymizeEntriesArgs,
|
||||
alias_parts: tuple[list[str], list[str]],
|
||||
csv_contents: CsvContents,
|
||||
):
|
||||
reuse_aliases = "Generated Alias" in csv_contents[0]
|
||||
|
||||
if reuse_aliases:
|
||||
print("Reusing generated aliases")
|
||||
else:
|
||||
for row in csv_contents:
|
||||
random = Random("; ".join([row["Email Address"], args.csv, args.files]))
|
||||
row["Generated Alias"] = (
|
||||
f"{random.choice(alias_parts[0])} {random.choice(alias_parts[0])}"
|
||||
)
|
||||
print("Generated an alias for each entry")
|
||||
|
||||
if args.dry_run:
|
||||
print("Dry run - not writing generated aliases back to CSV")
|
||||
else:
|
||||
with open(args.csv, "w", newline="") as csvfile:
|
||||
writer = csv.DictWriter(csvfile, fieldnames=csv_contents[0].keys())
|
||||
writer.writeheader()
|
||||
for row in csv_contents:
|
||||
writer.writerow(row)
|
||||
print("Wrote generated aliases back to CSV")
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
def maybe_remove_and_persist_resubmitted_entries(csv_contents: CsvContents):
|
||||
print("STUB - maybe_remove_and_persist_resubmitted_entries")
|
||||
|
||||
|
||||
def extract_entries_to_temporary_folder(
|
||||
args: AnonymizeEntriesArgs, csv_contents: CsvContents
|
||||
):
|
||||
print("STUB - extract_entries_to_temporary_folder")
|
||||
|
||||
|
||||
def anonymize_entries(
|
||||
args: AnonymizeEntriesArgs,
|
||||
csv_contents: CsvContents,
|
||||
temp_dir: Optional[TemporaryDirectory],
|
||||
):
|
||||
print("STUB - anonymize_entries")
|
||||
|
||||
|
||||
def zip_anonymized_entries(
|
||||
args: AnonymizeEntriesArgs,
|
||||
csv_contents: CsvContents,
|
||||
temp_dir: Optional[TemporaryDirectory],
|
||||
):
|
||||
print("STUB - zip_anonymized_entries")
|
||||
|
||||
|
||||
###############
|
||||
# Main method #
|
||||
###############
|
||||
|
||||
|
||||
def main(argv: list[str]):
|
||||
args = argparser().parse_args(argv[1:], namespace=AnonymizeEntriesArgs())
|
||||
assert_valid_file_paths(args)
|
||||
alias_parts = load_alias_parts("aliasparts.csv")
|
||||
csv_contents = load_csv_contents(args)
|
||||
assert_known_google_forms_columns_present(csv_contents)
|
||||
filename_column = detect_filename_column(csv_contents)
|
||||
maybe_generate_and_persist_aliases(args, alias_parts, csv_contents)
|
||||
|
||||
# Everything above is implemented; everything below is a stub
|
||||
|
||||
maybe_remove_and_persist_resubmitted_entries(csv_contents)
|
||||
temp_folder = extract_entries_to_temporary_folder(args, csv_contents)
|
||||
anonymize_entries(args, csv_contents, temp_folder)
|
||||
zip_anonymized_entries(args, csv_contents, temp_folder)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main(sys.argv)
|
207
poetry.lock
generated
Normal file
207
poetry.lock
generated
Normal file
|
@ -0,0 +1,207 @@
|
|||
# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand.
|
||||
|
||||
[[package]]
|
||||
name = "appdirs"
|
||||
version = "1.4.4"
|
||||
description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"},
|
||||
{file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "black"
|
||||
version = "24.8.0"
|
||||
description = "The uncompromising code formatter."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "black-24.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:09cdeb74d494ec023ded657f7092ba518e8cf78fa8386155e4a03fdcc44679e6"},
|
||||
{file = "black-24.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:81c6742da39f33b08e791da38410f32e27d632260e599df7245cccee2064afeb"},
|
||||
{file = "black-24.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:707a1ca89221bc8a1a64fb5e15ef39cd755633daa672a9db7498d1c19de66a42"},
|
||||
{file = "black-24.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:d6417535d99c37cee4091a2f24eb2b6d5ec42b144d50f1f2e436d9fe1916fe1a"},
|
||||
{file = "black-24.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fb6e2c0b86bbd43dee042e48059c9ad7830abd5c94b0bc518c0eeec57c3eddc1"},
|
||||
{file = "black-24.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:837fd281f1908d0076844bc2b801ad2d369c78c45cf800cad7b61686051041af"},
|
||||
{file = "black-24.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:62e8730977f0b77998029da7971fa896ceefa2c4c4933fcd593fa599ecbf97a4"},
|
||||
{file = "black-24.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:72901b4913cbac8972ad911dc4098d5753704d1f3c56e44ae8dce99eecb0e3af"},
|
||||
{file = "black-24.8.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:7c046c1d1eeb7aea9335da62472481d3bbf3fd986e093cffd35f4385c94ae368"},
|
||||
{file = "black-24.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:649f6d84ccbae73ab767e206772cc2d7a393a001070a4c814a546afd0d423aed"},
|
||||
{file = "black-24.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2b59b250fdba5f9a9cd9d0ece6e6d993d91ce877d121d161e4698af3eb9c1018"},
|
||||
{file = "black-24.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:6e55d30d44bed36593c3163b9bc63bf58b3b30e4611e4d88a0c3c239930ed5b2"},
|
||||
{file = "black-24.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:505289f17ceda596658ae81b61ebbe2d9b25aa78067035184ed0a9d855d18afd"},
|
||||
{file = "black-24.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b19c9ad992c7883ad84c9b22aaa73562a16b819c1d8db7a1a1a49fb7ec13c7d2"},
|
||||
{file = "black-24.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1f13f7f386f86f8121d76599114bb8c17b69d962137fc70efe56137727c7047e"},
|
||||
{file = "black-24.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:f490dbd59680d809ca31efdae20e634f3fae27fba3ce0ba3208333b713bc3920"},
|
||||
{file = "black-24.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:eab4dd44ce80dea27dc69db40dab62d4ca96112f87996bca68cd75639aeb2e4c"},
|
||||
{file = "black-24.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3c4285573d4897a7610054af5a890bde7c65cb466040c5f0c8b732812d7f0e5e"},
|
||||
{file = "black-24.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e84e33b37be070ba135176c123ae52a51f82306def9f7d063ee302ecab2cf47"},
|
||||
{file = "black-24.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:73bbf84ed136e45d451a260c6b73ed674652f90a2b3211d6a35e78054563a9bb"},
|
||||
{file = "black-24.8.0-py3-none-any.whl", hash = "sha256:972085c618ee94f402da1af548a4f218c754ea7e5dc70acb168bfaca4c2542ed"},
|
||||
{file = "black-24.8.0.tar.gz", hash = "sha256:2500945420b6784c38b9ee885af039f5e7471ef284ab03fa35ecdde4688cd83f"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
click = ">=8.0.0"
|
||||
mypy-extensions = ">=0.4.3"
|
||||
packaging = ">=22.0"
|
||||
pathspec = ">=0.9.0"
|
||||
platformdirs = ">=2"
|
||||
|
||||
[package.extras]
|
||||
colorama = ["colorama (>=0.4.3)"]
|
||||
d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"]
|
||||
jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"]
|
||||
uvloop = ["uvloop (>=0.15.2)"]
|
||||
|
||||
[[package]]
|
||||
name = "click"
|
||||
version = "8.1.7"
|
||||
description = "Composable command line interface toolkit"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"},
|
||||
{file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
colorama = {version = "*", markers = "platform_system == \"Windows\""}
|
||||
|
||||
[[package]]
|
||||
name = "colorama"
|
||||
version = "0.4.6"
|
||||
description = "Cross-platform colored terminal text."
|
||||
optional = false
|
||||
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
|
||||
files = [
|
||||
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
|
||||
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fs"
|
||||
version = "2.4.16"
|
||||
description = "Python's filesystem abstraction layer"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "fs-2.4.16-py2.py3-none-any.whl", hash = "sha256:660064febbccda264ae0b6bace80a8d1be9e089e0a5eb2427b7d517f9a91545c"},
|
||||
{file = "fs-2.4.16.tar.gz", hash = "sha256:ae97c7d51213f4b70b6a958292530289090de3a7e15841e108fbe144f069d313"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
appdirs = ">=1.4.3,<1.5.0"
|
||||
setuptools = "*"
|
||||
six = ">=1.10,<2.0"
|
||||
|
||||
[package.extras]
|
||||
scandir = ["scandir (>=1.5,<2.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "msdparser"
|
||||
version = "2.0.0"
|
||||
description = "Robust & lightning fast MSD parser (StepMania file format)"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
{file = "msdparser-2.0.0-py3-none-any.whl", hash = "sha256:bbef8c89b2dc16183ba714354b093fff11a43469971c42972284c5046826002d"},
|
||||
{file = "msdparser-2.0.0.tar.gz", hash = "sha256:9bb6cf4b705c76850b1ce22823c768b62ba2bb63d1c2407bbe3e2c23e38be8e3"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mypy-extensions"
|
||||
version = "1.0.0"
|
||||
description = "Type system extensions for programs checked with the mypy type checker."
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
files = [
|
||||
{file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"},
|
||||
{file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "packaging"
|
||||
version = "24.1"
|
||||
description = "Core utilities for Python packages"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"},
|
||||
{file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pathspec"
|
||||
version = "0.12.1"
|
||||
description = "Utility library for gitignore style pattern matching of file paths."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"},
|
||||
{file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "platformdirs"
|
||||
version = "4.2.2"
|
||||
description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"},
|
||||
{file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"]
|
||||
test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"]
|
||||
type = ["mypy (>=1.8)"]
|
||||
|
||||
[[package]]
|
||||
name = "setuptools"
|
||||
version = "72.1.0"
|
||||
description = "Easily download, build, install, upgrade, and uninstall Python packages"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "setuptools-72.1.0-py3-none-any.whl", hash = "sha256:5a03e1860cf56bb6ef48ce186b0e557fdba433237481a9a625176c2831be15d1"},
|
||||
{file = "setuptools-72.1.0.tar.gz", hash = "sha256:8d243eff56d095e5817f796ede6ae32941278f542e0f941867cc05ae52b162ec"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.text (>=3.7)", "more-itertools (>=8.8)", "ordered-set (>=3.1.1)", "packaging (>=24)", "platformdirs (>=2.6.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"]
|
||||
doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"]
|
||||
test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "mypy (==1.11.*)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (<0.4)", "pytest-ruff (>=0.2.1)", "pytest-ruff (>=0.3.2)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
|
||||
|
||||
[[package]]
|
||||
name = "simfile"
|
||||
version = "2.1.1"
|
||||
description = "Modern simfile library for Python"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
{file = "simfile-2.1.1-py3-none-any.whl", hash = "sha256:133094a33cf6b841ca7e620880ef8354c56dd4aef6b2c77582de7f120f69c620"},
|
||||
{file = "simfile-2.1.1.tar.gz", hash = "sha256:a7885f8395cdd0f19cd79da34e5675da778aa0e8dda76543cf075e21b862c4b7"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
fs = ">=2.4.15,<2.5.0"
|
||||
msdparser = ">=2.0.0,<2.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "six"
|
||||
version = "1.16.0"
|
||||
description = "Python 2 and 3 compatibility utilities"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
|
||||
files = [
|
||||
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
|
||||
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
|
||||
]
|
||||
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = "^3.11"
|
||||
content-hash = "130aa8886dcc190a88f8094e7e74aefbddad10ce60ab3d63d74c126bf6b5cc04"
|
17
pyproject.toml
Normal file
17
pyproject.toml
Normal file
|
@ -0,0 +1,17 @@
|
|||
[tool.poetry]
|
||||
name = "dimocracy-voucher"
|
||||
version = "0.1.0"
|
||||
description = ""
|
||||
authors = ["ashastral <git@ashastral.com>"]
|
||||
readme = "README.md"
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.11"
|
||||
simfile = "^2.1.1"
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
black = "^24.8.0"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core"]
|
||||
build-backend = "poetry.core.masonry.api"
|
Loading…
Reference in a new issue