first commit

This commit is contained in:
2025-10-11 03:50:49 -05:00
parent fcdef3ffe1
commit 9e9668172c
353 changed files with 47535 additions and 0 deletions

0
events/__init__.py Normal file
View File

30
events/admin.py Normal file
View File

@@ -0,0 +1,30 @@
from django.contrib import admin
from .models import *
class ScraperAdmin(admin.ModelAdmin):
# prepopulated_fields = {"slug": ("shortname",)}
list_display = ("name", "items", "new_items", "last_ran")
class OrganizationAdmin(admin.ModelAdmin):
# prepopulated_fields = {"slug": ("shortname",)}
list_display = ( "name", "city",)
# list_filter = ("promo_type",)
class EventAdmin(admin.ModelAdmin):
# prepopulated_fields = {"slug": ("shortname",)}
list_display = ( "show_title", "event_type", "show_date",)
list_filter = ("venue", "event_type")
class PromoAdmin(admin.ModelAdmin):
# prepopulated_fields = {"slug": ("shortname",)}
list_display = ("title", "organization", "promo_type", "published")
list_filter = ("promo_type",)
# Register your models here.
admin.site.register(Scraper, ScraperAdmin)
admin.site.register(Event, EventAdmin)
admin.site.register(Organization, OrganizationAdmin)
admin.site.register(Promo, PromoAdmin)
admin.site.register(Calendar)

6
events/apps.py Normal file
View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class EventsConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'events'

258
events/digitools.py Normal file
View File

@@ -0,0 +1,258 @@
import os, sys
from datetime import datetime
from dateutil import relativedelta
from time import sleep
import pytz
from lxml import html
from pprint import pprint as ppr
import django
sys.path.append('../')
os.environ['DJANGO_SETTINGS_MODULE'] = 'ds_events.settings'
django.setup()
from xvfbwrapper import Xvfb
from selenium import webdriver as wd
from events.models import Event as DSEvent, Organization, Promo, Scraper, Calendar
tz = pytz.timezone("US/Central")
td = relativedelta.relativedelta(months=1)
odt = datetime.now() + td
def getScraper(venue):
try:
scraper, created = Scraper.objects.get_or_create(
name=venue.name,
website=venue.website,
calendar = Calendar.objects.get(id=1),
items = 0,
new_items = 0,
last_ran = datetime.now(),
)
except Exception as e:
print(e)
scraper = Scraper.objects.get(name=venue.name)
num_of_events = DSEvent.objects.filter(scraper=scraper)
scraper.items = len(num_of_events)
scraper.save()
print("Scraper: ", scraper)
pass
return scraper, scraper.items
def updateScraper(scraper, item_count_start):
num_of_events = DSEvent.objects.filter(scraper=scraper)
scraper.items = len(num_of_events)
scraper.new_items = len(num_of_events) - item_count_start
scraper.last_ran = datetime.now()
scraper.save()
return
def getSource(browser, link):
browser.get(link)
sleep(5)
ps = html.fromstring(browser.page_source)
return ps
def getBrowser(run_env):
if run_env == 'dev':
print("Chrome is a go!")
# chromeOptions = wd.ChromeOptions()
# chromeOptions.binary_location = "/Application/Google\ Chrome.app"
# chromeDriver = "/opt/homebrew/bin/chromedriver"
# br = wd.Chrome(chromeDriver, options=chromeOptions)
br = wd.Chrome()
return br
elif run_env == "def":
print("Firefox go vroom")
br = wd.Firefox()
return br
elif run_env == "prod":
start_cmd = "Xvfb :91 && export DISPLAY=:91 &"
xvfb = Xvfb()
os.system(start_cmd)
xvfb.start()
print("started Xvfb")
br = wd.Firefox()
return br
else:
print("Failed", sys.argv, arg1)
quit()
def createBasicURL(site_url):
month = datetime.now().month
next_month = odt.month
year = datetime.now().year
links = [
site_url + str(month) + "/" + str(year),
site_url + str(next_month) + "/" + str(year)
]
return links
def createURLNoZero(site_url):
month = datetime.now().month
next_month = odt.month
year = datetime.now().year
links = [
site_url + str(year) + "/" + str(month),
]
if next_month == "1":
links.append(site_url + str(int(year)+1) + "/" + str(next_month))
else:
links.append(site_url + str(year) + "/" + str(next_month))
return links
def createURL(site_url):
month = datetime.now().month
if month < 10:
month = "0" + str(month)
else:
month = str(month)
next_month = odt.month
if next_month < 10:
next_month = "0" + str(next_month)
else:
next_month = str(next_month)
year = datetime.now().year
links = [
site_url + str(year) + "/" + month,
]
if next_month == "01":
links.append(site_url + str(int(year)+1) + "/" + next_month)
else:
links.append(site_url + str(year) + "/" + next_month)
return links
def createDashURL(site_url):
month = datetime.now().month
if month < 10:
month = "0" + str(month)
else:
month = str(month)
next_month = odt.month
if next_month < 10:
next_month = "0" + str(next_month)
else:
next_month = str(next_month)
year = datetime.now().year
links = [
site_url + month + "-" + str(year),
site_url + next_month + "-" + str(year)
]
print(links)
return links
def createBasicEvent(event, event_type, venue):
new_event, created = DSEvent.objects.update_or_create(
event_type = event_type,
show_title = event['title'],
show_link = event['link'],
show_date = event['dateStamp'],
show_day = event['dateStamp'],
calendar = event['calendar'],
scraper = event['scraper'],
venue = venue
)
return new_event, created
def createBasiciCalEvent(event, event_type, venue):
print("starting create")
ppr(event)
new_event, created = DSEvent.objects.update_or_create(
event_type = event_type,
show_title = event['title'][0],
show_link = event['link'],
show_date = datetime.strptime(str(event['dateStamp'][0]), '%Y-%m-%d %H:%M:%S'),
show_day = datetime.strptime(str(event['dateStamp'][0]), '%Y-%m-%d %H:%M:%S'),
calendar = event['calendar'],
scraper = event['scraper'],
venue = venue
)
print("created")
return new_event, created
def createDetailedEvent(event, event_type, venue, scraper):
new_event, created = DSEvent.objects.update_or_create(
event_type = event_type,
show_title = event["show_title"],
show_link = event["link"],
show_date = event["dateStamp"],
show_day = event["dateStamp"],
guests = " ".join(event["guests"]),
more_details = event["details"],
calendar = event['calendar'],
scraper = event['scraper'],
venue = venue
)
return new_event, created
def createBasicArticle(article, event_type, organization):
new_article, created = Promo.objects.update_or_create(
promo_type = 'Ja',
title = article['title'],
target_link = article['link'],
published = True,
organization = organization
)
return new_article, created
def getiCalEvents(gcal, scraper):
for component in gcal.walk():
event = {}
event['scraper'] = scraper
event['calendar'] = scraper.calendar
event['strSummary'] = f"{(component.get('SUMMARY'))}"
event['strDesc'] = component.get('DESCRIPTION')
event['strLocation'] = component.get('LOCATION')
event['dateStart'] = component.get('DTSTART')
event['dateStamp'] = component.get('DTSTAMP')
if event['dateStamp'] is not None:
event['dateStamp'] = event['dateStamp'].dt
if event['dateStart'] is not None:
try:
event['dateStart'] = event['dateStart'].dt
except Exception as e:
event['dateStart'] = event['dateStart'].dt
event['dateEnd'] = (component.get('DTEND'))
if event['dateEnd'] is not None:
event['dateEnd'] = event['dateEnd'].dt
else:
event['dateEnd'] = event['dateStart']
if event['strSummary'] != 'None':
event['details'] = {
"description" : event['strDesc'],
"Location" : event['strLocation'],
}
now_now = datetime.today().date()
try:
print("1Event: ", event['dateStart'])
if event['dateStart'] > now_now:
new_date = event['dateStart']-td
new_event = {}
new_event['scraper'] = scraper
new_event['calendar'] = scraper.calendar
new_event['title'] = event['strSummary'],
new_event['date'] = str(new_date),
new_event['dateStamp'] = str(new_date),
new_event['link'] = venue.website
createBasiciCalEvent(new_event, "Mu", venue)
except Exception as e:
try:
event['dateStart'] = event['dateStart'].date()
print("1Event: ", event['dateStart'])
if event['dateStart'] > now_now:
new_date = event['dateStart']-td
print("The new Date: ", new_date, type(new_date))
new_event = {}
new_event['scraper'] = scraper
new_event['calendar'] = scraper.calendar
new_event['title'] = event['strSummary'],
new_event['date'] = new_date,
new_event['dateStamp'] = new_date,
new_event['link'] = venue.website
createBasiciCalEvent(new_event, "Mu", venue)
except Exception as e:
print("The Error: ", e)
pass

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,452 @@
[
{
"model": "events.promo",
"pk": 1,
"fields": {
"title": "DreamFreely",
"organization": 1,
"promo_type": "Jo",
"overlay_image": "promo/SOL_Sign.png",
"short_text": "And intro to the operation.",
"long_text": "<p>Alright, I guess this is it. This is the game, these are the plays.</p>\r\n\r\n<p>Lots of work, for sure; but it's a blessing to help people. Now to continue to expand the support and stability.</p>",
"target_link": "https://www.dreamfreely.org",
"notes": "",
"published": true
}
},
{
"model": "events.promo",
"pk": 2,
"fields": {
"title": "Comuna Andina",
"organization": 1,
"promo_type": "Fo",
"overlay_image": "promo/pa_cover.jpg",
"short_text": "Authentic products from the Andes Mountains and surrounding regions, mochilas, cafe y panella.",
"long_text": "<p>These are all products from my travels.</p>\r\n<p>From hand-woven mochilas, to organic mountain farmed coffee and panela and more.</p>\r\n<p>All of these products are direct from the producer, while nearly all proceeds are also returned to the producer.</p>",
"target_link": "https://www.comunandina.com",
"notes": "",
"published": true
}
},
{
"model": "events.promo",
"pk": 3,
"fields": {
"title": "idioke",
"organization": 1,
"promo_type": "Ev",
"overlay_image": "promo/soltoken.png",
"short_text": "We're starting with English, but soon you will be able to practice Spanish as well.",
"long_text": "We're starting with English, but soon you will be able to practice Spanish as well.We're starting with English, but soon you will be able to practice Spanish as well.We're starting with English, but soon you will be able to practice Spanish as well.",
"target_link": "https://www.idioke.com",
"notes": "",
"published": true
}
},
{
"model": "events.promo",
"pk": 4,
"fields": {
"title": "Manifesting Empathy",
"organization": 1,
"promo_type": "Fo",
"overlay_image": "promo/manifestingempathy.png",
"short_text": "Help humans find their roots.",
"long_text": "Help humans find their roots.",
"target_link": "https://www.manifestingempathy.com",
"notes": "",
"published": true
}
},
{
"model": "events.promo",
"pk": 5,
"fields": {
"title": "DigiSnaxx LIVE!",
"organization": 1,
"promo_type": "Re",
"overlay_image": "promo/cover.png",
"short_text": "@ the Acadia. Every Monday. 4pm.",
"long_text": "This is a brave space to converse, relax, listen to music and begin to navigate a path forward.<br/><br/>This is going to be a process.",
"target_link": "https://canin.dreamfreely.org/digisnaxx/",
"notes": "",
"published": true
}
},
{
"model": "events.promo",
"pk": 6,
"fields": {
"title": "AI & the Last Question",
"organization": 1,
"promo_type": "Fo",
"overlay_image": "promo/cover.png",
"short_text": "A short story by Isaac Asimov",
"long_text": "A short story by Isaac AsimovA short story by Isaac AsimovA short story by Isaac Asimov",
"target_link": "https://canin.dreamfreely.org/the-last-question/",
"notes": "",
"published": true
}
},
{
"model": "events.promo",
"pk": 7,
"fields": {
"title": "idioke",
"organization": 1,
"promo_type": "Ev",
"overlay_image": "promo/soltoken.png",
"short_text": "We're starting with English, but soon you will be able to practice Spanish as well.",
"long_text": "We're starting with English, but soon you will be able to practice Spanish as well.We're starting with English, but soon you will be able to practice Spanish as well.We're starting with English, but soon you will be able to practice Spanish as well.",
"target_link": "https://www.idioke.com",
"notes": "",
"published": true
}
},
{
"model": "events.promo",
"pk": 8,
"fields": {
"title": "AI & the Last Question",
"organization": 1,
"promo_type": "Re",
"overlay_image": "promo/cover.png",
"short_text": "A short story by Isaac Asimov",
"long_text": "A short story by Isaac AsimovA short story by Isaac AsimovA short story by Isaac Asimov",
"target_link": "https://canin.dreamfreely.org/the-last-question/",
"notes": "",
"published": true
}
},
{
"model": "events.promo",
"pk": 9,
"fields": {
"title": "Comuna Andina",
"organization": 1,
"promo_type": "Ev",
"overlay_image": "promo/pa_cover.jpg",
"short_text": "Authentic products from the Andes Mountains and surrounding regions, mochilas, cafe y panella.",
"long_text": "<p>These are all products from my travels.</p>\r\n<p>From hand-woven mochilas, to organic mountain farmed coffee and panela and more.</p>\r\n<p>All of these products are direct from the producer, while nearly all proceeds are also returned to the producer.</p>",
"target_link": "https://www.comunandina.com",
"notes": "",
"published": true
}
},
{
"model": "events.promo",
"pk": 10,
"fields": {
"title": "DreamFreely",
"organization": 1,
"promo_type": "Re",
"overlay_image": "promo/SOL_Sign.png",
"short_text": "And intro to the operation.",
"long_text": "<p>Alright, I guess this is it. This is the game, these are the plays.</p>\r\n\r\n<p>Lots of work, for sure; but it's a blessing to help people. Now to continue to expand the support and stability.</p>",
"target_link": "https://www.dreamfreely.org",
"notes": "",
"published": true
}
},
{
"model": "events.promo",
"pk": 11,
"fields": {
"title": "Manifesting Empathy",
"organization": 1,
"promo_type": "Ev",
"overlay_image": "promo/manifestingempathy.png",
"short_text": "Help humans find their roots.",
"long_text": "Help humans find their roots.",
"target_link": "https://www.manifestingempathy.com",
"notes": "",
"published": true
}
},
{
"model": "events.promo",
"pk": 12,
"fields": {
"title": "DigiSnaxx & the DBC",
"organization": 1,
"promo_type": "Ev",
"overlay_image": "promo/cover.png",
"short_text": "More info about the project DigiSnaxx.",
"long_text": "<p>After seeing the City Pages fall down the drain, followed by the dissolution of the MetroIBA.</p>\r\n<p>Anywho, it's time for something different, and that's what DigiSnaxx and DreamFreely is all about.</p>\r\n<p>DigiSnaxx is not trying to replace either of the aforementioned entities; we are rather looking to be an evolution, of sorts.</p>\r\n</p>We're not trying to be everything either ...</p>\r\n<p>We're trying to be an accessible, community-centered, directory.</p>",
"target_link": "https://canin.dreamfreely.org/digisnaxx/",
"notes": "",
"published": true
}
},
{
"model": "events.promo",
"pk": 13,
"fields": {
"title": "Complimentary Street Harrassment",
"organization": 1,
"promo_type": "An",
"overlay_image": "",
"short_text": "It's cultural right?",
"long_text": "I learn about the creation of gender in public space through the lens of a PhD student studying pidopo's, in Colombia.\r\n\r\n<iframe style='border-radius:12px' src='https://open.spotify.com/embed/episode/4V3V9lAS4FRz7apdfj1qsV?utm_source=generator' width='100%' height='352' frameBorder='0' allowfullscreen='' allow='autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture' loading='lazy'></iframe>",
"target_link": "https://creators.spotify.com/pod/show/digisnaxx/episodes/Complimentary-Street-Harassment-et9h5d",
"notes": "",
"published": true
}
},
{
"model": "events.promo",
"pk": 14,
"fields": {
"title": "DigiSnaxx & the DBC",
"organization": 1,
"promo_type": "Fo",
"overlay_image": "promo/cover.png",
"short_text": "More info about the project DigiSnaxx.",
"long_text": "<p>After seeing the City Pages fall down the drain, followed by the dissolution of the MetroIBA.</p>\r\n<p>Anywho, it's time for something different, and that's what DigiSnaxx and DreamFreely is all about.</p>\r\n<p>DigiSnaxx is not trying to replace either of the aforementioned entities; we are rather looking to be an evolution, of sorts.</p>\r\n</p>We're not trying to be everything either ...</p>\r\n<p>We're trying to be an accessible, community-centered, directory.</p>",
"target_link": "https://canin.dreamfreely.org/digisnaxx/",
"notes": "",
"published": true
}
},
{
"model": "events.promo",
"pk": 15,
"fields": {
"title": "DreamFreely Library",
"organization": 1,
"promo_type": "Re",
"overlay_image": "",
"short_text": "It's like having an open notebook ...",
"long_text": "It's a work in progress, but you get the idea; and there's still some useful information.",
"target_link": "https://library.dreamfreely.org",
"notes": "",
"published": true
}
},
{
"model": "events.promo",
"pk": 16,
"fields": {
"title": "Talkin' w/ DiViNCi",
"organization": 1,
"promo_type": "Fo",
"overlay_image": "promo/soltoken.png",
"short_text": "Canin converses with DiViNCi.",
"long_text": "We met a great many number of years ago; before the hills were hills and the trees mere saplings. Haha ... I dunno, but it was definitely a fun conversation.\r\n\r\n<iframe style='border-radius:12px' src='https://open.spotify.com/embed/episode/2gdzy6TjXd3QEj8WRSeZSD?utm_source=generator' width='100%' height='352' frameBorder='0' allowfullscreen='' allow='autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture' loading='lazy'></iframe>",
"target_link": "https://creators.spotify.com/pod/show/digisnaxx/episodes/DiViNCi-egm90v",
"notes": "",
"published": true
}
},
{
"model": "events.promo",
"pk": 17,
"fields": {
"title": "Mpls Stp Mag Calendar",
"organization": 1,
"promo_type": "Ev",
"overlay_image": "",
"short_text": "They got a great list of events.",
"long_text": "They've got a great collection of events; we just don't have the time/resources to parse them all at present; and so pass the link directly on to you.",
"target_link": "https://calendar.mspmag.com/calendars/all-events",
"notes": "",
"published": true
}
},
{
"model": "events.promo",
"pk": 19,
"fields": {
"title": "Academia Nuts",
"organization": 1,
"promo_type": "Re",
"overlay_image": "promo/SOL_Sign.png",
"short_text": "Abstacts coming soon; gotta catch 'em all.",
"long_text": "I've always wanted to make academia more accessible, so here's my go at that!\r\n\r\n<iframe class='airtable-embed' src='https://airtable.com/embed/appzQxsifc8AnD1zA/shr3SATN1OTsY47it' frameborder='0' onmousewheel='' width='100%' height='533' style='background: transparent; border: 1px solid #ccc;'></iframe>",
"target_link": "https://www.academianuts.net",
"notes": "",
"published": true
}
},
{
"model": "events.promo",
"pk": 20,
"fields": {
"title": "Talkin' w/ DiViNCi",
"organization": 1,
"promo_type": "Fo",
"overlay_image": "promo/soltoken.png",
"short_text": "Canin converses with DiViNCi.",
"long_text": "We met a great many number of years ago; before the hills were hills and the trees mere saplings. Haha ... I dunno, but it was definitely a fun conversation.\r\n\r\n<iframe style='border-radius:12px' src='https://open.spotify.com/embed/episode/2gdzy6TjXd3QEj8WRSeZSD?utm_source=generator' width='100%' height='352' frameBorder='0' allowfullscreen='' allow='autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture' loading='lazy'></iframe>",
"target_link": "https://creators.spotify.com/pod/show/digisnaxx/episodes/DiViNCi-egm90v",
"notes": "",
"published": true
}
},
{
"model": "events.promo",
"pk": 21,
"fields": {
"title": "Rebel Coding",
"organization": 1,
"promo_type": "Re",
"overlay_image": "",
"short_text": "Enough knowledge to be dangerous.",
"long_text": "<p>Just covering the basics, we'll be hosting webinars.</p>\r\n<p>HTML, CSS, JavaScript & Python.</p>",
"target_link": "https://www.rebelcoding.com",
"notes": "",
"published": true
}
},
{
"model": "events.promo",
"pk": 22,
"fields": {
"title": "Add a Calendar",
"organization": 1,
"promo_type": "Re",
"overlay_image": "",
"short_text": "Got a calendar for us?",
"long_text": "<iframe class='airtable-embed' src='https://airtable.com/embed/appzQxsifc8AnD1zA/shrfUvOiFdaHI8xoz' frameborder='0' onmousewheel='' width='100%'' height='533' style='background: transparent; border: 1px solid #ccc;'></iframe>",
"target_link": "",
"notes": "",
"published": true
}
},
{
"model": "events.promo",
"pk": 23,
"fields": {
"title": "Add a Calendar",
"organization": 1,
"promo_type": "Re",
"overlay_image": "",
"short_text": "Got a calendar for us?",
"long_text": "<iframe class='airtable-embed' src='https://airtable.com/embed/appzQxsifc8AnD1zA/shrfUvOiFdaHI8xoz' frameborder='0' onmousewheel='' width='100%'' height='533' style='background: transparent; border: 1px solid #ccc;'></iframe>",
"target_link": "",
"notes": "",
"published": true
}
},
{
"model": "events.promo",
"pk": 24,
"fields": {
"title": "Academia Nuts",
"organization": 1,
"promo_type": "Re",
"overlay_image": "promo/SOL_Sign.png",
"short_text": "Abstacts coming soon; gotta catch 'em all.",
"long_text": "I've always wanted to make academia more accessible, so here's my go at that!\r\n\r\n<iframe class='airtable-embed' src='https://airtable.com/embed/appzQxsifc8AnD1zA/shr3SATN1OTsY47it' frameborder='0' onmousewheel='' width='100%' height='533' style='background: transparent; border: 1px solid #ccc;'></iframe>",
"target_link": "https://www.academianuts.net",
"notes": "",
"published": true
}
},
{
"model": "events.promo",
"pk": 25,
"fields": {
"title": "Rebel Coding",
"organization": 1,
"promo_type": "Re",
"overlay_image": "",
"short_text": "Enough knowledge to be dangerous.",
"long_text": "<p>Just covering the basics, we'll be hosting webinars.</p>\r\n<p>HTML, CSS, JavaScript & Python.</p>",
"target_link": "https://www.rebelcoding.com",
"notes": "",
"published": true
}
},
{
"model": "events.promo",
"pk": 26,
"fields": {
"title": "Comuna Andina",
"organization": 1,
"promo_type": "Fo",
"overlay_image": "promo/pa_cover.jpg",
"short_text": "Authentic products from the Andes Mountains and surrounding regions, mochilas, cafe y panella.",
"long_text": "<p>These are all products from my travels.</p>\r\n<p>From hand-woven mochilas, to organic mountain farmed coffee and panela and more.</p>\r\n<p>All of these products are direct from the producer, while nearly all proceeds are also returned to the producer.</p>",
"target_link": "https://www.comunandina.com",
"notes": "",
"published": true
}
},
{
"model": "events.promo",
"pk": 27,
"fields": {
"title": "Add a Calendar",
"organization": 1,
"promo_type": "Re",
"overlay_image": "",
"short_text": "Got a calendar for us?",
"long_text": "<iframe class='airtable-embed' src='https://airtable.com/embed/appzQxsifc8AnD1zA/shrfUvOiFdaHI8xoz' frameborder='0' onmousewheel='' width='100%'' height='533' style='background: transparent; border: 1px solid #ccc;'></iframe>",
"target_link": "",
"notes": "",
"published": true
}
},
{
"model": "events.promo",
"pk": 28,
"fields": {
"title": "Complimentary Street Harrassment",
"organization": 1,
"promo_type": "An",
"overlay_image": "",
"short_text": "It's cultural right?",
"long_text": "I learn about the creation of gender in public space through the lens of a PhD student studying pidopo's, in Colombia.\r\n\r\n<iframe style='border-radius:12px' src='https://open.spotify.com/embed/episode/4V3V9lAS4FRz7apdfj1qsV?utm_source=generator' width='100%' height='352' frameBorder='0' allowfullscreen='' allow='autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture' loading='lazy'></iframe>",
"target_link": "https://creators.spotify.com/pod/show/digisnaxx/episodes/Complimentary-Street-Harassment-et9h5d",
"notes": "",
"published": true
}
},
{
"model": "events.promo",
"pk": 29,
"fields": {
"title": "Saint Wich Burgers",
"organization": 1,
"promo_type": "Fo",
"overlay_image": "promo/soltoken.png",
"short_text": "Serving handcrafted gourmet burgers made with love.",
"long_text": "Welcome to Saint Wich Burgers, located on Selby Avenue in Saint Paul, Minnesota, where our love for food and dedication to quality come together in every burger we serve. We dont believe in shortcuts. Our burgers are made from scratch with premium ingredients, served fresh, and customized to suit your unique tastes.\r\n<br/><br/>\r\nFrom our hand-crafted patties to our delicious signature sauces, everything is designed to make each bite something special. Whether you like your burger simple or stacked with all the toppings, we offer a variety of options to satisfy every craving.\r\n<br/><br/>\r\nCome see what makes us different. At Saint Wich Burgers, it's all about great burgers, good times, and lasting memories.\r\n<br/><br/>\r\nWhether you're in the mood for a simple, classic burger or a sandwich with sides, weve got you covered. Enjoy the perfect meal in our inviting space, where you can savor your burger and enjoy time with family and friends.\r\n<br/><br/>\r\nOur atmosphere is laid-back, our service is friendly, and our burgers are unforgettable. Stop by today and taste what makes us different!",
"target_link": "https://www.stwichburgers.com/",
"notes": "",
"published": false
}
},
{
"model": "events.promo",
"pk": 30,
"fields": {
"title": "Arepas, Las de Queso",
"organization": 1,
"promo_type": "Re",
"overlay_image": "promo/SOL_Sign.png",
"short_text": "If you're lookin' for the tastiest arepa in Medellin.",
"long_text": "For those who may travel, check out my friends :)",
"target_link": "https://www.dreamfreely.org",
"notes": "",
"published": false
}
},
{
"model": "events.promo",
"pk": 31,
"fields": {
"title": "Vigs Guitars",
"organization": 1,
"promo_type": "Re",
"overlay_image": "promo/VigGuitarsLogo.sm.jpg",
"short_text": "A luthier-owned music shop.",
"long_text": "<b>“The Players Store”</b>\r\n<br/><br/>\r\nWe are an independent, full service, luthier-owned shop serving the working musicians in the Minneapolis/St. Paul metro area since September 2014. Ted Vigs expert repair is the cornerstone of our business. We specialize in repair and customization, and carry a variety of guitars, basses, mandolins, ukuleles, and accessories.\r\n<br/><br/>\r\nWith EXPERT repair, a large stock of parts and interesting, unique and fun instruments, both new and used, you wont be afraid to come in here, and its a big part of the reason that weve been coined as “The Players Store.”\r\n<br/><br/>\r\nTed Vig has been working full time and building his audience through music stores since 1988. He has a long list of devoted repair clients…this just doesnt happen overnight! His Custom Vig Handwound Pickups are flying out the door! *SATURDAYS ARE THE BEST DAYS TO COME IN AND TALK TO TED ABOUT THE PICKUPS*\r\n<br/><br/>\r\nThis store is Indigenous Female Owned and run by local musicians who SUPPORT local musicians! We have ample street parking in front of the shop and a big parking lot.\r\n<br/><br/>\r\nWinner of “Star Tribunes Readers Choice Best of”\r\nBest Music Instrument Shop\r\n<br/><br/>\r\n2021 SILVER! 2023 SILVER!\r\n<br/>\r\n2022 GOLD 2024 GOLD!!!!",
"target_link": "https://vigguitarshop.com/",
"notes": "",
"published": false
}
}
]

452
events/fixtures/promo.json Normal file
View File

@@ -0,0 +1,452 @@
[
{
"model": "events.promo",
"pk": 1,
"fields": {
"title": "DreamFreely",
"organization": 1,
"promo_type": "Jo",
"overlay_image": "promo/SOL_Sign.png",
"short_text": "And intro to the operation.",
"long_text": "<p>Alright, I guess this is it. This is the game, these are the plays.</p>\r\n\r\n<p>Lots of work, for sure; but it's a blessing to help people. Now to continue to expand the support and stability.</p>",
"target_link": "https://www.dreamfreely.org",
"notes": "",
"published": true
}
},
{
"model": "events.promo",
"pk": 2,
"fields": {
"title": "Comuna Andina",
"organization": 1,
"promo_type": "Fo",
"overlay_image": "promo/pa_cover.jpg",
"short_text": "Authentic products from the Andes Mountains and surrounding regions, mochilas, cafe y panella.",
"long_text": "<p>These are all products from my travels.</p>\r\n<p>From hand-woven mochilas, to organic mountain farmed coffee and panela and more.</p>\r\n<p>All of these products are direct from the producer, while nearly all proceeds are also returned to the producer.</p>",
"target_link": "https://www.comunandina.com",
"notes": "",
"published": true
}
},
{
"model": "events.promo",
"pk": 3,
"fields": {
"title": "idioke",
"organization": 1,
"promo_type": "Ev",
"overlay_image": "promo/soltoken.png",
"short_text": "We're starting with English, but soon you will be able to practice Spanish as well.",
"long_text": "We're starting with English, but soon you will be able to practice Spanish as well.We're starting with English, but soon you will be able to practice Spanish as well.We're starting with English, but soon you will be able to practice Spanish as well.",
"target_link": "https://www.idioke.com",
"notes": "",
"published": true
}
},
{
"model": "events.promo",
"pk": 4,
"fields": {
"title": "Manifesting Empathy",
"organization": 1,
"promo_type": "Fo",
"overlay_image": "promo/manifestingempathy.png",
"short_text": "Help humans find their roots.",
"long_text": "Help humans find their roots.",
"target_link": "https://www.manifestingempathy.com",
"notes": "",
"published": true
}
},
{
"model": "events.promo",
"pk": 5,
"fields": {
"title": "AI & the Last Question",
"organization": 1,
"promo_type": "Fo",
"overlay_image": "promo/cover.png",
"short_text": "A short story by Isaac Asimov",
"long_text": "A short story by Isaac AsimovA short story by Isaac AsimovA short story by Isaac Asimov",
"target_link": "https://canin.dreamfreely.org/the-last-question/",
"notes": "",
"published": true
}
},
{
"model": "events.promo",
"pk": 6,
"fields": {
"title": "AI & the Last Question",
"organization": 1,
"promo_type": "Fo",
"overlay_image": "promo/cover.png",
"short_text": "A short story by Isaac Asimov",
"long_text": "A short story by Isaac AsimovA short story by Isaac AsimovA short story by Isaac Asimov",
"target_link": "https://canin.dreamfreely.org/the-last-question/",
"notes": "",
"published": true
}
},
{
"model": "events.promo",
"pk": 7,
"fields": {
"title": "idioke",
"organization": 1,
"promo_type": "Ev",
"overlay_image": "promo/soltoken.png",
"short_text": "We're starting with English, but soon you will be able to practice Spanish as well.",
"long_text": "We're starting with English, but soon you will be able to practice Spanish as well.We're starting with English, but soon you will be able to practice Spanish as well.We're starting with English, but soon you will be able to practice Spanish as well.",
"target_link": "https://www.idioke.com",
"notes": "",
"published": true
}
},
{
"model": "events.promo",
"pk": 8,
"fields": {
"title": "AI & the Last Question",
"organization": 1,
"promo_type": "Re",
"overlay_image": "promo/cover.png",
"short_text": "A short story by Isaac Asimov",
"long_text": "A short story by Isaac AsimovA short story by Isaac AsimovA short story by Isaac Asimov",
"target_link": "https://canin.dreamfreely.org/the-last-question/",
"notes": "",
"published": true
}
},
{
"model": "events.promo",
"pk": 9,
"fields": {
"title": "Comuna Andina",
"organization": 1,
"promo_type": "Ev",
"overlay_image": "promo/pa_cover.jpg",
"short_text": "Authentic products from the Andes Mountains and surrounding regions, mochilas, cafe y panella.",
"long_text": "<p>These are all products from my travels.</p>\r\n<p>From hand-woven mochilas, to organic mountain farmed coffee and panela and more.</p>\r\n<p>All of these products are direct from the producer, while nearly all proceeds are also returned to the producer.</p>",
"target_link": "https://www.comunandina.com",
"notes": "",
"published": true
}
},
{
"model": "events.promo",
"pk": 10,
"fields": {
"title": "DreamFreely",
"organization": 1,
"promo_type": "Re",
"overlay_image": "promo/SOL_Sign.png",
"short_text": "And intro to the operation.",
"long_text": "<p>Alright, I guess this is it. This is the game, these are the plays.</p>\r\n\r\n<p>Lots of work, for sure; but it's a blessing to help people. Now to continue to expand the support and stability.</p>",
"target_link": "https://www.dreamfreely.org",
"notes": "",
"published": true
}
},
{
"model": "events.promo",
"pk": 11,
"fields": {
"title": "Manifesting Empathy",
"organization": 1,
"promo_type": "Ev",
"overlay_image": "promo/manifestingempathy.png",
"short_text": "Help humans find their roots.",
"long_text": "Help humans find their roots.",
"target_link": "https://www.manifestingempathy.com",
"notes": "",
"published": true
}
},
{
"model": "events.promo",
"pk": 12,
"fields": {
"title": "DigiSnaxx & the DBC",
"organization": 1,
"promo_type": "Ev",
"overlay_image": "promo/cover.png",
"short_text": "More info about the project DigiSnaxx.",
"long_text": "<p>After seeing the City Pages fall down the drain, followed by the dissolution of the MetroIBA.</p>\r\n<p>Anywho, it's time for something different, and that's what DigiSnaxx and DreamFreely is all about.</p>\r\n<p>DigiSnaxx is not trying to replace either of the aforementioned entities; we are rather looking to be an evolution, of sorts.</p>\r\n</p>We're not trying to be everything either ...</p>\r\n<p>We're trying to be an accessible, community-centered, directory.</p>",
"target_link": "https://canin.dreamfreely.org/digisnaxx/",
"notes": "",
"published": true
}
},
{
"model": "events.promo",
"pk": 13,
"fields": {
"title": "Complimentary Street Harrassment",
"organization": 1,
"promo_type": "An",
"overlay_image": "",
"short_text": "It's cultural right?",
"long_text": "I learn about the creation of gender in public space through the lens of a PhD student studying pidopo's, in Colombia.\r\n\r\n<iframe style='border-radius:12px' src='https://open.spotify.com/embed/episode/4V3V9lAS4FRz7apdfj1qsV?utm_source=generator' width='100%' height='352' frameBorder='0' allowfullscreen='' allow='autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture' loading='lazy'></iframe>",
"target_link": "https://creators.spotify.com/pod/show/digisnaxx/episodes/Complimentary-Street-Harassment-et9h5d",
"notes": "",
"published": true
}
},
{
"model": "events.promo",
"pk": 14,
"fields": {
"title": "DigiSnaxx & the DBC",
"organization": 1,
"promo_type": "Fo",
"overlay_image": "promo/cover.png",
"short_text": "More info about the project DigiSnaxx.",
"long_text": "<p>After seeing the City Pages fall down the drain, followed by the dissolution of the MetroIBA.</p>\r\n<p>Anywho, it's time for something different, and that's what DigiSnaxx and DreamFreely is all about.</p>\r\n<p>DigiSnaxx is not trying to replace either of the aforementioned entities; we are rather looking to be an evolution, of sorts.</p>\r\n</p>We're not trying to be everything either ...</p>\r\n<p>We're trying to be an accessible, community-centered, directory.</p>",
"target_link": "https://canin.dreamfreely.org/digisnaxx/",
"notes": "",
"published": true
}
},
{
"model": "events.promo",
"pk": 15,
"fields": {
"title": "DreamFreely Library",
"organization": 1,
"promo_type": "Re",
"overlay_image": "",
"short_text": "It's like having an open notebook ...",
"long_text": "It's a work in progress, but you get the idea; and there's still some useful information.",
"target_link": "https://library.dreamfreely.org",
"notes": "",
"published": true
}
},
{
"model": "events.promo",
"pk": 16,
"fields": {
"title": "Talkin' w/ DiViNCi",
"organization": 1,
"promo_type": "Fo",
"overlay_image": "promo/soltoken.png",
"short_text": "Canin converses with DiViNCi.",
"long_text": "We met a great many number of years ago; before the hills were hills and the trees mere saplings. Haha ... I dunno, but it was definitely a fun conversation.\r\n\r\n<iframe style='border-radius:12px' src='https://open.spotify.com/embed/episode/2gdzy6TjXd3QEj8WRSeZSD?utm_source=generator' width='100%' height='352' frameBorder='0' allowfullscreen='' allow='autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture' loading='lazy'></iframe>",
"target_link": "https://creators.spotify.com/pod/show/digisnaxx/episodes/DiViNCi-egm90v",
"notes": "",
"published": true
}
},
{
"model": "events.promo",
"pk": 17,
"fields": {
"title": "Mpls Stp Mag Calendar",
"organization": 1,
"promo_type": "Ev",
"overlay_image": "",
"short_text": "They got a great list of events.",
"long_text": "They've got a great collection of events; we just don't have the time/resources to parse them all at present; and so pass the link directly on to you.",
"target_link": "https://calendar.mspmag.com/calendars/all-events",
"notes": "",
"published": true
}
},
{
"model": "events.promo",
"pk": 19,
"fields": {
"title": "Academia Nuts",
"organization": 1,
"promo_type": "Re",
"overlay_image": "promo/SOL_Sign.png",
"short_text": "Abstacts coming soon; gotta catch 'em all.",
"long_text": "I've always wanted to make academia more accessible, so here's my go at that!\r\n\r\n<iframe class='airtable-embed' src='https://airtable.com/embed/appzQxsifc8AnD1zA/shr3SATN1OTsY47it' frameborder='0' onmousewheel='' width='100%' height='533' style='background: transparent; border: 1px solid #ccc;'></iframe>",
"target_link": "https://www.academianuts.net",
"notes": "",
"published": true
}
},
{
"model": "events.promo",
"pk": 20,
"fields": {
"title": "Talkin' w/ DiViNCi",
"organization": 1,
"promo_type": "Fo",
"overlay_image": "promo/soltoken.png",
"short_text": "Canin converses with DiViNCi.",
"long_text": "We met a great many number of years ago; before the hills were hills and the trees mere saplings. Haha ... I dunno, but it was definitely a fun conversation.\r\n\r\n<iframe style='border-radius:12px' src='https://open.spotify.com/embed/episode/2gdzy6TjXd3QEj8WRSeZSD?utm_source=generator' width='100%' height='352' frameBorder='0' allowfullscreen='' allow='autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture' loading='lazy'></iframe>",
"target_link": "https://creators.spotify.com/pod/show/digisnaxx/episodes/DiViNCi-egm90v",
"notes": "",
"published": true
}
},
{
"model": "events.promo",
"pk": 21,
"fields": {
"title": "Rebel Coding",
"organization": 1,
"promo_type": "Re",
"overlay_image": "",
"short_text": "Enough knowledge to be dangerous.",
"long_text": "<p>Just covering the basics, we'll be hosting webinars.</p>\r\n<p>HTML, CSS, JavaScript & Python.</p>",
"target_link": "https://www.rebelcoding.com",
"notes": "",
"published": true
}
},
{
"model": "events.promo",
"pk": 22,
"fields": {
"title": "Add a Calendar",
"organization": 1,
"promo_type": "Re",
"overlay_image": "",
"short_text": "Got a calendar for us?",
"long_text": "<iframe class='airtable-embed' src='https://airtable.com/embed/appzQxsifc8AnD1zA/shrfUvOiFdaHI8xoz' frameborder='0' onmousewheel='' width='100%'' height='533' style='background: transparent; border: 1px solid #ccc;'></iframe>",
"target_link": "",
"notes": "",
"published": true
}
},
{
"model": "events.promo",
"pk": 23,
"fields": {
"title": "Add a Calendar",
"organization": 1,
"promo_type": "Re",
"overlay_image": "",
"short_text": "Got a calendar for us?",
"long_text": "<iframe class='airtable-embed' src='https://airtable.com/embed/appzQxsifc8AnD1zA/shrfUvOiFdaHI8xoz' frameborder='0' onmousewheel='' width='100%'' height='533' style='background: transparent; border: 1px solid #ccc;'></iframe>",
"target_link": "",
"notes": "",
"published": true
}
},
{
"model": "events.promo",
"pk": 24,
"fields": {
"title": "Academia Nuts",
"organization": 1,
"promo_type": "Re",
"overlay_image": "promo/SOL_Sign.png",
"short_text": "Abstacts coming soon; gotta catch 'em all.",
"long_text": "I've always wanted to make academia more accessible, so here's my go at that!\r\n\r\n<iframe class='airtable-embed' src='https://airtable.com/embed/appzQxsifc8AnD1zA/shr3SATN1OTsY47it' frameborder='0' onmousewheel='' width='100%' height='533' style='background: transparent; border: 1px solid #ccc;'></iframe>",
"target_link": "https://www.academianuts.net",
"notes": "",
"published": true
}
},
{
"model": "events.promo",
"pk": 25,
"fields": {
"title": "Rebel Coding",
"organization": 1,
"promo_type": "Re",
"overlay_image": "",
"short_text": "Enough knowledge to be dangerous.",
"long_text": "<p>Just covering the basics, we'll be hosting webinars.</p>\r\n<p>HTML, CSS, JavaScript & Python.</p>",
"target_link": "https://www.rebelcoding.com",
"notes": "",
"published": true
}
},
{
"model": "events.promo",
"pk": 26,
"fields": {
"title": "Comuna Andina",
"organization": 1,
"promo_type": "Fo",
"overlay_image": "promo/pa_cover.jpg",
"short_text": "Authentic products from the Andes Mountains and surrounding regions, mochilas, cafe y panella.",
"long_text": "<p>These are all products from my travels.</p>\r\n<p>From hand-woven mochilas, to organic mountain farmed coffee and panela and more.</p>\r\n<p>All of these products are direct from the producer, while nearly all proceeds are also returned to the producer.</p>",
"target_link": "https://www.comunandina.com",
"notes": "",
"published": true
}
},
{
"model": "events.promo",
"pk": 27,
"fields": {
"title": "Add a Calendar",
"organization": 1,
"promo_type": "Re",
"overlay_image": "",
"short_text": "Got a calendar for us?",
"long_text": "<iframe class='airtable-embed' src='https://airtable.com/embed/appzQxsifc8AnD1zA/shrfUvOiFdaHI8xoz' frameborder='0' onmousewheel='' width='100%'' height='533' style='background: transparent; border: 1px solid #ccc;'></iframe>",
"target_link": "",
"notes": "",
"published": true
}
},
{
"model": "events.promo",
"pk": 28,
"fields": {
"title": "Complimentary Street Harrassment",
"organization": 1,
"promo_type": "An",
"overlay_image": "",
"short_text": "It's cultural right?",
"long_text": "I learn about the creation of gender in public space through the lens of a PhD student studying pidopo's, in Colombia.\r\n\r\n<iframe style='border-radius:12px' src='https://open.spotify.com/embed/episode/4V3V9lAS4FRz7apdfj1qsV?utm_source=generator' width='100%' height='352' frameBorder='0' allowfullscreen='' allow='autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture' loading='lazy'></iframe>",
"target_link": "https://creators.spotify.com/pod/show/digisnaxx/episodes/Complimentary-Street-Harassment-et9h5d",
"notes": "",
"published": true
}
},
{
"model": "events.promo",
"pk": 29,
"fields": {
"title": "Saint Wich Burgers",
"organization": 1,
"promo_type": "Fo",
"overlay_image": "promo/soltoken.png",
"short_text": "Serving handcrafted gourmet burgers made with love.",
"long_text": "Welcome to Saint Wich Burgers, located on Selby Avenue in Saint Paul, Minnesota, where our love for food and dedication to quality come together in every burger we serve. We dont believe in shortcuts. Our burgers are made from scratch with premium ingredients, served fresh, and customized to suit your unique tastes.\r\n<br/><br/>\r\nFrom our hand-crafted patties to our delicious signature sauces, everything is designed to make each bite something special. Whether you like your burger simple or stacked with all the toppings, we offer a variety of options to satisfy every craving.\r\n<br/><br/>\r\nCome see what makes us different. At Saint Wich Burgers, it's all about great burgers, good times, and lasting memories.\r\n<br/><br/>\r\nWhether you're in the mood for a simple, classic burger or a sandwich with sides, weve got you covered. Enjoy the perfect meal in our inviting space, where you can savor your burger and enjoy time with family and friends.\r\n<br/><br/>\r\nOur atmosphere is laid-back, our service is friendly, and our burgers are unforgettable. Stop by today and taste what makes us different!",
"target_link": "https://www.stwichburgers.com/",
"notes": "",
"published": false
}
},
{
"model": "events.promo",
"pk": 30,
"fields": {
"title": "Arepas, Las de Queso",
"organization": 1,
"promo_type": "Re",
"overlay_image": "promo/SOL_Sign.png",
"short_text": "If you're lookin' for the tastiest arepa in Medellin.",
"long_text": "For those who may travel, check out my friends :)",
"target_link": "https://www.dreamfreely.org",
"notes": "",
"published": false
}
},
{
"model": "events.promo",
"pk": 31,
"fields": {
"title": "Vigs Guitars",
"organization": 1,
"promo_type": "Re",
"overlay_image": "promo/VigGuitarsLogo.sm.jpg",
"short_text": "A luthier-owned music shop.",
"long_text": "<b>“The Players Store”</b>\r\n<br/><br/>\r\nWe are an independent, full service, luthier-owned shop serving the working musicians in the Minneapolis/St. Paul metro area since September 2014. Ted Vigs expert repair is the cornerstone of our business. We specialize in repair and customization, and carry a variety of guitars, basses, mandolins, ukuleles, and accessories.\r\n<br/><br/>\r\nWith EXPERT repair, a large stock of parts and interesting, unique and fun instruments, both new and used, you wont be afraid to come in here, and its a big part of the reason that weve been coined as “The Players Store.”\r\n<br/><br/>\r\nTed Vig has been working full time and building his audience through music stores since 1988. He has a long list of devoted repair clients…this just doesnt happen overnight! His Custom Vig Handwound Pickups are flying out the door! *SATURDAYS ARE THE BEST DAYS TO COME IN AND TALK TO TED ABOUT THE PICKUPS*\r\n<br/><br/>\r\nThis store is Indigenous Female Owned and run by local musicians who SUPPORT local musicians! We have ample street parking in front of the shop and a big parking lot.\r\n<br/><br/>\r\nWinner of “Star Tribunes Readers Choice Best of”\r\nBest Music Instrument Shop\r\n<br/><br/>\r\n2021 SILVER! 2023 SILVER!\r\n<br/>\r\n2022 GOLD 2024 GOLD!!!!",
"target_link": "https://vigguitarshop.com/",
"notes": "",
"published": false
}
}
]

View File

@@ -0,0 +1 @@
[{"model": "events.venue", "pk": 1, "fields": {"name": "Acme Comedy Club", "website": "https://acmecomedycompany.com/the-club/calendar/", "phone_number": null, "address": null, "city": "Minneapolis", "state": "Minnesota", "zip_code": null}}, {"model": "events.venue", "pk": 2, "fields": {"name": "Amsterdam Bar & Hall", "website": "https://www.amsterdambarandhall.com/events-new/", "phone_number": null, "address": null, "city": "St. Paul", "state": null, "zip_code": null}}, {"model": "events.venue", "pk": 3, "fields": {"name": "Birchbark Books", "website": "https://birchbarkbooks.com/pages/events", "phone_number": null, "address": null, "city": "Minneapolis", "state": "Minnesota", "zip_code": null}}, {"model": "events.venue", "pk": 4, "fields": {"name": "Cedar Cultural Center", "website": "https://www.thecedar.org/listing", "phone_number": null, "address": null, "city": "Minneapolis", "state": null, "zip_code": null}}, {"model": "events.venue", "pk": 5, "fields": {"name": "331 Club", "website": null, "phone_number": null, "address": null, "city": null, "state": null, "zip_code": null}}, {"model": "events.venue", "pk": 6, "fields": {"name": "Comedy Corner", "website": "https://comedycornerunderground.com/calendar", "phone_number": null, "address": null, "city": "Minneapolis", "state": null, "zip_code": null}}, {"model": "events.venue", "pk": 7, "fields": {"name": "Eastside Freedom Library", "website": "https://eastsidefreedomlibrary.org/events/", "phone_number": null, "address": null, "city": "Minneapolis", "state": null, "zip_code": null}}, {"model": "events.venue", "pk": 30, "fields": {"name": "7th St Entry", "website": null, "phone_number": null, "address": null, "city": null, "state": null, "zip_code": null}}, {"model": "events.venue", "pk": 34, "fields": {"name": "Fine Line", "website": null, "phone_number": null, "address": null, "city": null, "state": null, "zip_code": null}}, {"model": "events.venue", "pk": 35, "fields": {"name": "The Fitzgerald Theater", "website": null, "phone_number": null, "address": null, "city": null, "state": null, "zip_code": null}}, {"model": "events.venue", "pk": 36, "fields": {"name": "Turf Club", "website": null, "phone_number": null, "address": null, "city": null, "state": null, "zip_code": null}}, {"model": "events.venue", "pk": 37, "fields": {"name": "Palace Theatre", "website": null, "phone_number": null, "address": null, "city": null, "state": null, "zip_code": null}}, {"model": "events.venue", "pk": 38, "fields": {"name": "First Avenue", "website": null, "phone_number": null, "address": null, "city": null, "state": null, "zip_code": null}}, {"model": "events.venue", "pk": 39, "fields": {"name": "The Cedar Cultural Center", "website": null, "phone_number": null, "address": null, "city": null, "state": null, "zip_code": null}}, {"model": "events.venue", "pk": 40, "fields": {"name": "Pantages Theatre", "website": null, "phone_number": null, "address": null, "city": null, "state": null, "zip_code": null}}, {"model": "events.venue", "pk": 41, "fields": {"name": "Xcel Energy Center", "website": null, "phone_number": null, "address": null, "city": null, "state": null, "zip_code": null}}, {"model": "events.venue", "pk": 42, "fields": {"name": "State Theatre", "website": null, "phone_number": null, "address": null, "city": null, "state": null, "zip_code": null}}, {"model": "events.venue", "pk": 43, "fields": {"name": "Hook & Ladder", "website": null, "phone_number": null, "address": null, "city": null, "state": null, "zip_code": null}}, {"model": "events.venue", "pk": 44, "fields": {"name": "Magers & Quinn", "website": "https://www.magersandquinn.com/events", "phone_number": null, "address": null, "city": "Minneapolis", "state": null, "zip_code": null}}, {"model": "events.venue", "pk": 45, "fields": {"name": "Uptown VFW", "website": null, "phone_number": null, "address": null, "city": "Minneapolis", "state": null, "zip_code": null}}, {"model": "events.venue", "pk": 46, "fields": {"name": "Palmer's Bar", "website": "https://palmers-bar.com", "phone_number": null, "address": null, "city": "Minneapolis", "state": null, "zip_code": null}}, {"model": "events.venue", "pk": 47, "fields": {"name": "Parkway Theater", "website": "https://theparkwaytheater.com", "phone_number": null, "address": null, "city": "Minneapolis", "state": null, "zip_code": null}}, {"model": "events.venue", "pk": 48, "fields": {"name": "White Squirrel", "website": "https://whitesquirrelbar.com", "phone_number": null, "address": null, "city": "St. Paul", "state": null, "zip_code": null}}]

View File

@@ -0,0 +1,42 @@
# Generated by Django 4.1.7 on 2023-02-28 16:07
import django.core.files.storage
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Venue',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=64)),
('website', models.CharField(max_length=128)),
('phone_number', models.CharField(max_length=200)),
('address', models.CharField(max_length=64)),
('city', models.CharField(max_length=32)),
('state', models.CharField(max_length=16)),
('zip_code', models.CharField(max_length=16)),
],
),
migrations.CreateModel(
name='Event',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('show_title', models.CharField(max_length=128)),
('show_link', models.URLField()),
('guests', models.CharField(max_length=256)),
('show_date', models.DateTimeField()),
('flyer_img', models.ImageField(upload_to=django.core.files.storage.FileSystemStorage(location='/media/flyers'))),
('more_details', models.JSONField()),
('venue', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='events.venue')),
],
),
]

View File

@@ -0,0 +1,19 @@
# Generated by Django 4.1.7 on 2023-03-01 07:10
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('events', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='event',
name='event_type',
field=models.CharField(default='Mu', max_length=128),
preserve_default=False,
),
]

View File

@@ -0,0 +1,82 @@
# Generated by Django 4.1.7 on 2023-03-01 08:13
import django.core.files.storage
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('events', '0002_event_event_type'),
]
operations = [
migrations.AlterModelOptions(
name='event',
options={'ordering': ['show_title'], 'verbose_name_plural': 'Events'},
),
migrations.AlterModelOptions(
name='venue',
options={'ordering': ['name'], 'verbose_name_plural': 'Venues'},
),
migrations.AlterField(
model_name='event',
name='event_type',
field=models.CharField(choices=[('Mu', 'Music'), ('Va', 'Visual Art'), ('Gv', 'Government'), ('Ce', 'Civic Engagement'), ('Ed', 'Educational')], default='0', max_length=16),
),
migrations.AlterField(
model_name='event',
name='flyer_img',
field=models.ImageField(blank=True, null=True, upload_to=django.core.files.storage.FileSystemStorage(location='/media/flyers')),
),
migrations.AlterField(
model_name='event',
name='guests',
field=models.CharField(blank=True, max_length=256, null=True),
),
migrations.AlterField(
model_name='event',
name='more_details',
field=models.JSONField(blank=True, null=True),
),
migrations.AlterField(
model_name='event',
name='show_link',
field=models.URLField(blank=True, null=True),
),
migrations.AlterField(
model_name='event',
name='show_title',
field=models.CharField(blank=True, max_length=128, null=True),
),
migrations.AlterField(
model_name='venue',
name='address',
field=models.CharField(blank=True, max_length=64, null=True),
),
migrations.AlterField(
model_name='venue',
name='city',
field=models.CharField(blank=True, max_length=32, null=True),
),
migrations.AlterField(
model_name='venue',
name='phone_number',
field=models.CharField(blank=True, max_length=200, null=True),
),
migrations.AlterField(
model_name='venue',
name='state',
field=models.CharField(blank=True, max_length=16, null=True),
),
migrations.AlterField(
model_name='venue',
name='website',
field=models.CharField(blank=True, max_length=128, null=True),
),
migrations.AlterField(
model_name='venue',
name='zip_code',
field=models.CharField(blank=True, max_length=16, null=True),
),
]

View File

@@ -0,0 +1,27 @@
# Generated by Django 4.1.7 on 2023-03-06 01:45
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('events', '0003_alter_event_options_alter_venue_options_and_more'),
]
operations = [
migrations.AlterModelOptions(
name='event',
options={'ordering': ['show_date', 'show_title'], 'verbose_name_plural': 'Events'},
),
migrations.AddField(
model_name='event',
name='show_day',
field=models.DateField(blank=True, null=True),
),
migrations.AlterField(
model_name='event',
name='event_type',
field=models.CharField(choices=[('Mu', 'Music'), ('Va', 'Visual Art'), ('Gv', 'Government'), ('Ce', 'Civic Engagement'), ('Ed', 'Educational'), ('Co', 'Comedy'), ('Ma', 'Mutual Aid')], default='0', max_length=16),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 4.1.7 on 2023-03-23 03:50
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('events', '0004_alter_event_options_event_show_day_and_more'),
]
operations = [
migrations.AddField(
model_name='event',
name='img_link',
field=models.CharField(blank=True, max_length=256, null=True),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 4.1.7 on 2023-03-25 13:43
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('events', '0005_event_img_link'),
]
operations = [
migrations.AlterField(
model_name='event',
name='event_type',
field=models.CharField(choices=[('Mu', 'Music'), ('Va', 'Visual Art'), ('Gv', 'Government'), ('Ce', 'Civic Engagement'), ('Ed', 'Educational'), ('Co', 'Comedy'), ('Ma', 'Mutual Aid'), ('Th', 'Theater')], default='0', max_length=16),
),
]

View File

@@ -0,0 +1,39 @@
# Generated by Django 4.1.7 on 2023-05-07 21:41
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('events', '0006_alter_event_event_type'),
]
operations = [
migrations.AlterField(
model_name='event',
name='event_type',
field=models.CharField(choices=[('Mu', 'Music'), ('Ot', 'Other'), ('Va', 'Visual Art'), ('Gv', 'Government'), ('Ce', 'Civic Engagement'), ('Ed', 'Educational'), ('Co', 'Comedy'), ('Ma', 'Mutual Aid'), ('Th', 'Theater')], default='0', max_length=16),
),
migrations.CreateModel(
name='UserThrottle',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('scope', models.CharField(choices=[('admin', 'Admin'), ('platinum', 'Platinum'), ('gold', 'Gold'), ('silver', 'Silver'), ('free', 'Free')], max_length=20)),
('calls', models.IntegerField(default=0)),
('limit', models.IntegerField(default=0)),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
name='UserScope',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('scope', models.CharField(choices=[('admin', 'Admin'), ('platinum', 'Platinum'), ('gold', 'Gold'), ('silver', 'Silver'), ('free', 'Free')], max_length=20)),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
]

View File

@@ -0,0 +1,59 @@
# Generated by Django 5.1.1 on 2024-11-24 06:03
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('events', '0007_alter_event_event_type_userthrottle_userscope'),
]
operations = [
migrations.CreateModel(
name='Organization',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=64)),
('website', models.CharField(blank=True, max_length=128, null=True)),
('is_venue', models.BooleanField(default=False)),
('is_501c', models.BooleanField(default=False)),
('contact_name', models.CharField(blank=True, max_length=64, null=True)),
('contact_email', models.CharField(blank=True, max_length=64, null=True)),
('phone_number', models.CharField(blank=True, max_length=200, null=True)),
('address', models.CharField(blank=True, max_length=64, null=True)),
('city', models.CharField(blank=True, max_length=32, null=True)),
('state', models.CharField(blank=True, max_length=16, null=True)),
('zip_code', models.CharField(blank=True, max_length=16, null=True)),
],
options={
'verbose_name_plural': 'Organizations',
'ordering': ['name'],
},
),
migrations.AlterField(
model_name='event',
name='venue',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='events.organization'),
),
migrations.CreateModel(
name='Promo',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=64)),
('promo_type', models.CharField(choices=[('Jo', 'Job Opening'), ('Re', 'Retail'), ('Fo', 'Food'), ('Ev', 'Event')], default='0', max_length=16)),
('image', models.ImageField(blank=True, null=True, upload_to='')),
('promo_text', models.TextField(blank=True, null=True)),
('target_link', models.URLField(blank=True, null=True)),
('notes', models.TextField(blank=True, null=True)),
('organization', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='events.organization')),
],
options={
'verbose_name_plural': 'Promo',
},
),
migrations.DeleteModel(
name='Venue',
),
]

View File

@@ -0,0 +1,27 @@
# Generated by Django 5.1.1 on 2024-11-24 08:28
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('events', '0008_organization_alter_event_venue_promo_delete_venue'),
]
operations = [
migrations.RemoveField(
model_name='promo',
name='image',
),
migrations.AddField(
model_name='promo',
name='desk_image',
field=models.ImageField(blank=True, upload_to='promo/desk'),
),
migrations.AddField(
model_name='promo',
name='mobile_image',
field=models.ImageField(blank=True, upload_to='promo/mobile'),
),
]

View File

@@ -0,0 +1,27 @@
# Generated by Django 5.1.1 on 2024-12-01 22:45
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('events', '0009_remove_promo_image_promo_desk_image_and_more'),
]
operations = [
migrations.RenameField(
model_name='promo',
old_name='promo_text',
new_name='promo_text_short',
),
migrations.RemoveField(
model_name='promo',
name='desk_image',
),
migrations.AddField(
model_name='promo',
name='promo_text_long',
field=models.TextField(blank=True, max_length=127, null=True),
),
]

View File

@@ -0,0 +1,32 @@
# Generated by Django 5.1.1 on 2024-12-11 07:07
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('events', '0010_rename_promo_text_promo_promo_text_short_and_more'),
]
operations = [
migrations.RenameField(
model_name='promo',
old_name='promo_text_long',
new_name='long_text',
),
migrations.RenameField(
model_name='promo',
old_name='promo_text_short',
new_name='short_text',
),
migrations.RemoveField(
model_name='promo',
name='mobile_image',
),
migrations.AddField(
model_name='promo',
name='image',
field=models.ImageField(blank=True, upload_to='promo'),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.1.1 on 2024-12-11 07:15
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('events', '0011_rename_promo_text_long_promo_long_text_and_more'),
]
operations = [
migrations.AlterField(
model_name='promo',
name='long_text',
field=models.TextField(blank=True, null=True),
),
]

View File

@@ -0,0 +1,17 @@
# Generated by Django 5.1.1 on 2025-01-19 18:50
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('events', '0012_alter_promo_long_text'),
]
operations = [
migrations.AlterUniqueTogether(
name='organization',
unique_together={('name', 'is_venue')},
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.1.1 on 2025-02-11 19:38
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('events', '0013_alter_organization_unique_together'),
]
operations = [
migrations.AddField(
model_name='promo',
name='published',
field=models.BooleanField(default=False),
),
]

View File

@@ -0,0 +1,33 @@
# Generated by Django 5.1.1 on 2025-02-12 01:39
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('events', '0014_promo_published'),
]
operations = [
migrations.RemoveField(
model_name='userthrottle',
name='user',
),
migrations.AddField(
model_name='organization',
name='membership',
field=models.CharField(choices=[('Nm', 'Non-Member'), ('Na', 'Nano Member'), ('Mm', 'Micro Member'), ('Sm', 'Small Business Member'), ('Lb', 'Local Business Member'), ('Rb', 'Regional Business Member')], default='0', max_length=24),
),
migrations.AlterField(
model_name='promo',
name='promo_type',
field=models.CharField(choices=[('Jo', 'Job Opening'), ('Re', 'Retail'), ('Fo', 'Food'), ('Ev', 'Event'), ('An', 'Academia Nuts'), ('Su', 'Survey Questions')], default='0', max_length=16),
),
migrations.DeleteModel(
name='UserScope',
),
migrations.DeleteModel(
name='UserThrottle',
),
]

View File

@@ -0,0 +1,32 @@
# Generated by Django 5.1.1 on 2025-02-25 01:27
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('events', '0015_remove_userthrottle_user_organization_membership_and_more'),
]
operations = [
migrations.AlterModelOptions(
name='promo',
options={'ordering': ['published', 'organization', 'title'], 'verbose_name_plural': 'Promo'},
),
migrations.AddField(
model_name='promo',
name='art_image',
field=models.ImageField(blank=True, upload_to='art'),
),
migrations.AddField(
model_name='promo',
name='embed_link',
field=models.CharField(blank=True, max_length=127, null=True),
),
migrations.AlterField(
model_name='promo',
name='promo_type',
field=models.CharField(choices=[('Jo', 'Job Opening'), ('Re', 'Retail'), ('Fo', 'Food'), ('Ev', 'Event'), ('Ma', 'Mutual Aid'), ('Ja', 'Journal Article'), ('Sp', 'Startup Pitch'), ('Ar', 'Art'), ('An', 'Academia Nuts'), ('Su', 'Survey Questions')], default='0', max_length=16),
),
]

View File

@@ -0,0 +1,70 @@
# Generated by Django 5.1.1 on 2025-02-28 00:37
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('events', '0016_alter_promo_options_promo_art_image_promo_embed_link_and_more'),
]
operations = [
migrations.RemoveField(
model_name='promo',
name='art_image',
),
migrations.RemoveField(
model_name='promo',
name='image',
),
migrations.AddField(
model_name='organization',
name='ein',
field=models.CharField(blank=True, max_length=16, null=True),
),
migrations.AddField(
model_name='organization',
name='long_desc',
field=models.TextField(blank=True, null=True),
),
migrations.AddField(
model_name='organization',
name='org_type',
field=models.CharField(choices=[('Fo', 'Food'), ('Re', 'Retail'), ('Se', 'Service'), ('Ud', 'Undefined')], default='3', max_length=24),
),
migrations.AddField(
model_name='organization',
name='short_desc',
field=models.CharField(blank=True, max_length=63, null=True),
),
migrations.AddField(
model_name='organization',
name='stripe_email',
field=models.CharField(blank=True, max_length=64, null=True),
),
migrations.AddField(
model_name='promo',
name='classified_image',
field=models.ImageField(blank=True, upload_to='classifieds'),
),
migrations.AddField(
model_name='promo',
name='overlay_image',
field=models.ImageField(blank=True, upload_to='overlays'),
),
migrations.AlterField(
model_name='event',
name='event_type',
field=models.CharField(choices=[('Ot', 'Other'), ('Mu', 'Music'), ('Va', 'Visual Art'), ('Gv', 'Government'), ('Ce', 'Civic Engagement'), ('Ed', 'Educational'), ('Ma', 'Mutual Aid'), ('Th', 'Theater'), ('Co', 'Comedy')], default='0', max_length=16),
),
migrations.AlterField(
model_name='promo',
name='promo_type',
field=models.CharField(choices=[('Ar', 'Art'), ('Fo', 'Food'), ('Ev', 'Event'), ('Re', 'Retail'), ('Ma', 'Mutual Aid'), ('Ca', 'Classifieds'), ('Jo', 'Job Opening'), ('Sp', 'Startup Pitch'), ('An', 'Academia Nuts'), ('Ja', 'Journal Article'), ('Su', 'Survey Questions')], default='0', max_length=16),
),
migrations.AlterUniqueTogether(
name='event',
unique_together={('show_title', 'show_date', 'venue')},
),
]

View File

@@ -0,0 +1,33 @@
# Generated by Django 5.1.1 on 2025-02-28 01:07
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('events', '0017_remove_promo_art_image_remove_promo_image_and_more'),
]
operations = [
migrations.CreateModel(
name='Scraper',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=64)),
('items', models.IntegerField()),
('last_run', models.DateTimeField()),
],
options={
'verbose_name_plural': 'Scrapers',
'ordering': ['name'],
'unique_together': {('name',)},
},
),
migrations.AddField(
model_name='event',
name='scraper',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='events.scraper'),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.1.1 on 2025-02-28 01:09
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('events', '0018_scraper_event_scraper'),
]
operations = [
migrations.AddField(
model_name='scraper',
name='website',
field=models.CharField(blank=True, max_length=64, null=True),
),
]

View File

@@ -0,0 +1,26 @@
# Generated by Django 5.1.1 on 2025-03-01 12:37
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('events', '0019_scraper_website'),
]
operations = [
migrations.RenameField(
model_name='scraper',
old_name='last_run',
new_name='last_ran',
),
migrations.AlterUniqueTogether(
name='event',
unique_together=set(),
),
migrations.AlterUniqueTogether(
name='organization',
unique_together=set(),
),
]

View File

@@ -0,0 +1,23 @@
# Generated by Django 5.1.1 on 2025-03-01 12:38
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('events', '0020_rename_last_run_scraper_last_ran_and_more'),
]
operations = [
migrations.AlterField(
model_name='scraper',
name='items',
field=models.IntegerField(blank=True, null=True),
),
migrations.AlterField(
model_name='scraper',
name='last_ran',
field=models.DateTimeField(blank=True, null=True),
),
]

View File

@@ -0,0 +1,21 @@
# Generated by Django 5.1.1 on 2025-03-02 00:49
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('events', '0021_alter_scraper_items_alter_scraper_last_ran'),
]
operations = [
migrations.AlterUniqueTogether(
name='organization',
unique_together={('name', 'is_venue')},
),
migrations.AlterUniqueTogether(
name='scraper',
unique_together={('name', 'website')},
),
]

View File

@@ -0,0 +1,22 @@
# Generated by Django 5.1.1 on 2025-03-02 01:08
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('events', '0022_alter_organization_unique_together_and_more'),
]
operations = [
migrations.AlterUniqueTogether(
name='scraper',
unique_together=set(),
),
migrations.AlterField(
model_name='scraper',
name='name',
field=models.CharField(max_length=64, unique=True),
),
]

View File

@@ -0,0 +1,36 @@
# Generated by Django 5.1.1 on 2025-03-21 01:27
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('events', '0023_alter_scraper_unique_together_alter_scraper_name'),
]
operations = [
migrations.CreateModel(
name='Tags',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=31, unique=True)),
('desc', models.TextField(blank=True, null=True)),
],
),
migrations.AddField(
model_name='event',
name='tags',
field=models.ManyToManyField(blank=True, null=True, to='events.tags'),
),
migrations.AddField(
model_name='organization',
name='tags',
field=models.ManyToManyField(blank=True, null=True, to='events.tags'),
),
migrations.AddField(
model_name='promo',
name='tags',
field=models.ManyToManyField(blank=True, null=True, to='events.tags'),
),
]

View File

@@ -0,0 +1,26 @@
# Generated by Django 5.1.1 on 2025-10-05 07:56
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('events', '0024_tags_event_tags_organization_tags_promo_tags'),
]
operations = [
migrations.CreateModel(
name='Calendars',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=31, unique=True)),
('desc', models.TextField(blank=True, null=True)),
],
),
migrations.AddField(
model_name='organization',
name='calendars',
field=models.ManyToManyField(blank=True, null=True, to='events.calendars'),
),
]

View File

@@ -0,0 +1,151 @@
# Generated by Django 5.1.1 on 2025-10-11 02:11
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('events', '0025_calendars_organization_calendars'),
]
operations = [
migrations.CreateModel(
name='Calendar',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=31, unique=True)),
('shortcode', models.CharField(max_length=3, unique=True)),
('desc', models.TextField(blank=True, null=True)),
],
),
migrations.RemoveField(
model_name='organization',
name='calendars',
),
migrations.AlterField(
model_name='event',
name='event_type',
field=models.CharField(choices=[('Ot', 'Other'), ('Mu', 'Music'), ('Va', 'Visual Art'), ('Gv', 'Government'), ('Ce', 'Civic Engagement'), ('Ed', 'Educational'), ('Ma', 'Mutual Aid'), ('Th', 'Theater'), ('Co', 'Comedy')], default='0', max_length=15),
),
migrations.AlterField(
model_name='event',
name='guests',
field=models.CharField(blank=True, max_length=255, null=True),
),
migrations.AlterField(
model_name='event',
name='img_link',
field=models.CharField(blank=True, max_length=255, null=True),
),
migrations.AlterField(
model_name='event',
name='show_title',
field=models.CharField(blank=True, max_length=127, null=True),
),
migrations.AlterField(
model_name='organization',
name='address',
field=models.CharField(blank=True, max_length=63, null=True),
),
migrations.AlterField(
model_name='organization',
name='city',
field=models.CharField(blank=True, max_length=31, null=True),
),
migrations.AlterField(
model_name='organization',
name='contact_email',
field=models.CharField(blank=True, max_length=63, null=True),
),
migrations.AlterField(
model_name='organization',
name='contact_name',
field=models.CharField(blank=True, max_length=63, null=True),
),
migrations.AlterField(
model_name='organization',
name='ein',
field=models.CharField(blank=True, max_length=15, null=True),
),
migrations.AlterField(
model_name='organization',
name='membership',
field=models.CharField(choices=[('Nm', 'Non-Member'), ('Na', 'Nano Member'), ('Mm', 'Micro Member'), ('Sm', 'Small Business Member'), ('Lb', 'Local Business Member'), ('Rb', 'Regional Business Member')], default='0', max_length=31),
),
migrations.AlterField(
model_name='organization',
name='name',
field=models.CharField(max_length=63),
),
migrations.AlterField(
model_name='organization',
name='org_type',
field=models.CharField(choices=[('Fo', 'Food'), ('Re', 'Retail'), ('Se', 'Service'), ('Ud', 'Undefined')], default='3', max_length=31),
),
migrations.AlterField(
model_name='organization',
name='phone_number',
field=models.CharField(blank=True, max_length=255, null=True),
),
migrations.AlterField(
model_name='organization',
name='state',
field=models.CharField(blank=True, max_length=15, null=True),
),
migrations.AlterField(
model_name='organization',
name='stripe_email',
field=models.CharField(blank=True, max_length=63, null=True),
),
migrations.AlterField(
model_name='organization',
name='website',
field=models.CharField(blank=True, max_length=126, null=True),
),
migrations.AlterField(
model_name='organization',
name='zip_code',
field=models.CharField(blank=True, max_length=15, null=True),
),
migrations.AlterField(
model_name='promo',
name='embed_link',
field=models.CharField(blank=True, max_length=126, null=True),
),
migrations.AlterField(
model_name='promo',
name='promo_type',
field=models.CharField(choices=[('Ar', 'Art'), ('Fo', 'Food'), ('Ev', 'Event'), ('Re', 'Retail'), ('Ma', 'Mutual Aid'), ('Ca', 'Classifieds'), ('Jo', 'Job Opening'), ('Sp', 'Startup Pitch'), ('An', 'Academia Nuts'), ('Ja', 'Journal Article'), ('Su', 'Survey Questions')], default='0', max_length=15),
),
migrations.AlterField(
model_name='promo',
name='title',
field=models.CharField(max_length=63),
),
migrations.AlterField(
model_name='scraper',
name='name',
field=models.CharField(max_length=63, unique=True),
),
migrations.AlterField(
model_name='scraper',
name='website',
field=models.CharField(blank=True, max_length=63, null=True),
),
migrations.AddField(
model_name='event',
name='calendar',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='events.calendar'),
),
migrations.AddField(
model_name='scraper',
name='calendar',
field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='events.calendar'),
preserve_default=False,
),
migrations.DeleteModel(
name='Calendars',
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.1.1 on 2025-10-11 02:41
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('events', '0026_calendar_remove_organization_calendars_and_more'),
]
operations = [
migrations.AddField(
model_name='scraper',
name='new_items',
field=models.IntegerField(blank=True, null=True),
),
]

View File

206
events/models.py Normal file
View File

@@ -0,0 +1,206 @@
from django.db import models
from django.core.files.storage import FileSystemStorage
from django.contrib.auth.models import User
fs = FileSystemStorage(location='/media/flyers')
# Create your models here.
class Calendar(models.Model):
name = models.CharField(max_length=31, unique=True)
shortcode = models.CharField(max_length=3, unique=True)
desc = models.TextField(blank=True, null=True)
def __unicode__(self):
return "%s" % self.shortcode
def __str__(self):
return u'%s' % self.shortcode
class Scraper(models.Model):
name = models.CharField(max_length=63, unique=True)
website = models.CharField(max_length=63, blank=True, null=True)
calendar = models.ForeignKey(Calendar, on_delete=models.CASCADE)
items = models.IntegerField(blank=True, null=True)
new_items = models.IntegerField(blank=True, null=True)
last_ran = models.DateTimeField(blank=True, null=True)
class Meta:
verbose_name_plural = "Scrapers"
ordering = ['name',]
def __unicode__(self):
return "%s" % self.name
def __str__(self):
return u'%s' % self.name
class Tags(models.Model):
name = models.CharField(max_length=31, unique=True)
desc = models.TextField(blank=True, null=True)
def __unicode__(self):
return "%s" % self.name
def __str__(self):
return u'%s' % self.name
class Organization(models.Model):
MEMBER_TYPE = (
('Nm', 'Non-Member'),
('Na', 'Nano Member'),
('Mm', 'Micro Member'),
('Sm', 'Small Business Member'),
('Lb', 'Local Business Member'),
('Rb', 'Regional Business Member'),
)
ORG_TYPE = (
('Fo', 'Food'),
('Re', 'Retail'),
('Se', 'Service'),
('Ud', 'Undefined'),
)
name = models.CharField(max_length=63)
website = models.CharField(max_length=126, blank=True, null=True)
membership = models.CharField(max_length=31, choices=MEMBER_TYPE, default='0')
org_type = models.CharField(max_length=31, choices=ORG_TYPE, default='3')
stripe_email = models.CharField(max_length=63, blank=True, null=True)
ein = models.CharField(max_length=15, blank=True, null=True)
is_venue= models.BooleanField(default=False)
is_501c = models.BooleanField(default=False)
short_desc = models.CharField(max_length=63, blank=True, null=True)
long_desc = models.TextField(blank=True, null=True)
contact_name = models.CharField(max_length=63, blank=True, null=True)
contact_email = models.CharField(max_length=63, blank=True, null=True)
phone_number = models.CharField(max_length=255, blank=True, null=True)
address = models.CharField(max_length=63, blank=True, null=True)
city = models.CharField(max_length=31, blank=True, null=True)
state = models.CharField(max_length=15, blank=True, null=True)
zip_code = models.CharField(max_length=15, blank=True, null=True)
tags = models.ManyToManyField(Tags, blank=True, null=True)
class Meta:
unique_together = ("name", "is_venue")
verbose_name_plural = "Organizations"
ordering = ['name']
def __unicode__(self):
return "%s" % self.name
def __str__(self):
return u'%s' % self.name
class Event(models.Model):
EVENT_TYPE = (
('Ot', 'Other'),
('Mu', 'Music'),
('Va', 'Visual Art'),
('Gv', 'Government'),
('Ce', 'Civic Engagement'),
('Ed', 'Educational'),
('Ma', 'Mutual Aid'),
('Th', 'Theater'),
('Co', 'Comedy'),
)
calendar = models.ForeignKey(Calendar, on_delete=models.CASCADE, blank=True, null=True)
scraper = models.ForeignKey(Scraper, on_delete=models.CASCADE, null=True)
venue = models.ForeignKey(Organization, on_delete=models.CASCADE)
event_type = models.CharField(max_length=15, choices=EVENT_TYPE, default='0')
show_title = models.CharField(max_length=127, blank=True, null=True)
show_link = models.URLField(blank=True, null=True)
guests = models.CharField(max_length=255, blank=True, null=True)
show_date = models.DateTimeField()
show_day = models.DateField(blank=True, null=True)
img_link = models.CharField(max_length=255, blank=True, null=True)
flyer_img = models.ImageField(upload_to=fs, blank=True, null=True)
more_details = models.JSONField(blank=True, null=True)
tags = models.ManyToManyField(Tags, blank=True, null=True)
class Meta:
verbose_name_plural = "Events"
# unique_together = ("show_title", "show_date", "venue")
ordering = ['show_date', 'show_title']
def __unicode__(self):
return "%s" % self.show_title
def __str__(self):
return u'%s' % self.show_title
class Promo(models.Model):
PROMO_TYPE = (
('Ar', 'Art'),
('Fo', 'Food'),
('Ev', 'Event'),
('Re', 'Retail'),
('Ma', 'Mutual Aid'),
('Ca', 'Classifieds'),
('Jo', 'Job Opening'),
('Sp', 'Startup Pitch'),
('An', 'Academia Nuts'),
('Ja', 'Journal Article'),
('Su', 'Survey Questions')
)
title = models.CharField(max_length=63)
organization = models.ForeignKey(Organization, on_delete=models.CASCADE)
promo_type = models.CharField(max_length=15, choices=PROMO_TYPE, default='0')
overlay_image = models.ImageField(upload_to="overlays", blank=True)
classified_image = models.ImageField(upload_to="classifieds", blank=True)
embed_link = models.CharField(max_length=126, blank=True, null=True)
short_text = models.TextField(blank=True, null=True)
long_text = models.TextField(blank=True, null=True)
target_link = models.URLField(blank=True, null=True)
notes = models.TextField(blank=True, null=True)
published = models.BooleanField(default=False)
tags = models.ManyToManyField(Tags, blank=True, null=True)
class Meta:
verbose_name_plural = "Promo"
ordering = ['published', 'organization', 'title',]
def __unicode__(self):
return "%s" % self.title
def __str__(self):
return u'%s' % self.title
# class UserThrottle(models.Model):
# user = models.ForeignKey(User, on_delete=models.CASCADE)
# scope = models.CharField(max_length=20, choices=(
# ('admin', 'Admin'),
# ('platinum', 'Platinum'),
# ('gold', 'Gold'),
# ('silver', 'Silver'),
# ('free', 'Free'),
# ))
# calls = models.IntegerField(default=0)
# limit = models.IntegerField(default=0)
# def __str__(self):
# return f"{self.user.username}: {self.scope}"
# class UserScope(models.Model):
# user = models.ForeignKey(User, on_delete=models.CASCADE)
# scope = models.CharField(max_length=20, choices=(
# ('admin', 'Admin'),
# ('platinum', 'Platinum'),
# ('gold', 'Gold'),
# ('silver', 'Silver'),
# ('free', 'Free'),
# ))
# def __str__(self):
# return f"{self.user.username}: {self.scope}"

62
events/serializers.py Normal file
View File

@@ -0,0 +1,62 @@
from rest_framework import serializers
from .models import Event, Organization, Promo
from django.db import models
from django.contrib.auth.models import User
from rest_framework.permissions import BasePermission
class ScopesPermission(BasePermission):
scopes_map = {
'admin': [],
'platinum': ['gold', 'silver', 'free'],
'gold': ['silver', 'free'],
'silver': ['free'],
'free': [],
}
def has_permission(self, request, view):
if not request.user.is_authenticated:
return False
# Check if the user has an associated scope
try:
user_scope = UserScope.objects.get(user=request.user)
except UserScope.DoesNotExist:
return False
# Check if the user's scope has the required permission level
if user_scope.scope not in self.scopes_map:
return False
allowed_scopes = self.scopes_map[user_scope.scope]
return request.scope in allowed_scopes or request.scope == user_scope.scope
############
## Events ##
############
class OrganizationSerializer(serializers.ModelSerializer):
class Meta:
model = Organization
fields = ('id', 'name', 'website', 'city')
# fields = '__all__'
class EventSerializer(serializers.ModelSerializer):
venue = OrganizationSerializer(many=False)
event_type = serializers.CharField(source='get_event_type_display')
# target_language = serializers.SerializerMethodField()
class Meta:
model = Event
fields = '__all__'
depth = 2
# fields = ('id', 'name',)
class PromoSerializer(serializers.ModelSerializer):
organization = OrganizationSerializer(many=False)
# event_type = serializers.CharField(source='get_event_type_display')
class Meta:
model = Promo
fields = ('id', 'title', 'organization', 'promo_type', 'long_text', 'short_text', 'overlay_image', 'classified_image', 'target_link')
# fields = '__all__'
depth = 2

3
events/tests.py Normal file
View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

25
events/urls.py Normal file
View File

@@ -0,0 +1,25 @@
"""ds_events URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/4.1/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path, include, re_path
from .views import *
urlpatterns = [
re_path(r'^events/', EventsAPIView.as_view(), name="get-events"),
re_path(r'^promo/', PromoAPIView.as_view(), name="get-promo"),
# re_path(r'^events-token/', EventsTokenAPIView.as_view(), name="get-token-events"),
]

52
events/views.py Normal file
View File

@@ -0,0 +1,52 @@
from django.shortcuts import render
from datetime import datetime, timedelta
import pytz, random
from .models import *
from .serializers import *
from django.db.models import Q
from rest_framework import generics
from rest_framework.decorators import authentication_classes, permission_classes
from rest_framework.authentication import SessionAuthentication, BasicAuthentication
from rest_framework.permissions import IsAuthenticated
# from durin.auth import TokenAuthentication
# from durin.views import APIAccessTokenView
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import filters
from rest_framework.response import Response
td = timedelta(hours=7)
odt = datetime.now() - td
# Create your views here.
class EventsAPIView(generics.ListAPIView):
serializer_class = EventSerializer
queryset = Event.objects.filter(show_date__gte=odt).order_by('show_date')
filter_backends = [DjangoFilterBackend, filters.SearchFilter]
filterset_fields = ['show_title', 'event_type', 'venue__name', 'calendar__shortcode']
search_fields = ['show_title', 'event_type', 'venue__name']
class PromoAPIView(generics.ListAPIView):
serializer_class = PromoSerializer
def get_queryset(self):
promo_objects = list(Promo.objects.filter(published=True))
print(len(promo_objects))
queryset = random.sample(promo_objects, 21)
return queryset
# class EventsTokenAPIView(APIAccessTokenView):
# serializer_class = EventSerializer
# authentication_classes = (TokenAuthentication, BasicAuthentication,)
# permission_classes = (IsAuthenticated,)
# queryset = Event.objects.filter(show_date__gte=odt).order_by('show_date')
# filter_backends = [DjangoFilterBackend, filters.SearchFilter]
# filterset_fields = ['show_title', 'event_type', 'show_date', 'show_day', 'venue__name']
# search_fields = ['show_title', 'event_type']