Add App Contracts
This commit is contained in:
0
contracts/__init__.py
Normal file
0
contracts/__init__.py
Normal file
15
contracts/admin.py
Normal file
15
contracts/admin.py
Normal file
@@ -0,0 +1,15 @@
|
||||
from django.contrib import admin
|
||||
from .models import *
|
||||
|
||||
class ContractAdmin(admin.ModelAdmin):
|
||||
# prepopulated_fields = {"slug": ("shortname",)}
|
||||
list_display = ("notice_id", "pub_date")
|
||||
|
||||
|
||||
|
||||
# Register your models here.
|
||||
admin.site.register(Paragraph)
|
||||
admin.site.register(OriginalContract)
|
||||
admin.site.register(Contract, ContractAdmin)
|
||||
admin.site.register(Company)
|
||||
|
||||
6
contracts/apps.py
Normal file
6
contracts/apps.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class ContractsConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'contracts'
|
||||
415
contracts/digitools.py
Normal file
415
contracts/digitools.py
Normal file
@@ -0,0 +1,415 @@
|
||||
import os, sys
|
||||
from datetime import datetime, timedelta
|
||||
from dateutil import relativedelta
|
||||
from time import sleep
|
||||
import pytz
|
||||
from lxml import html
|
||||
from pprint import pprint as ppr
|
||||
|
||||
import django
|
||||
|
||||
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")
|
||||
plus_one_month = relativedelta.relativedelta(months=1)
|
||||
odt_next_month = datetime.now() + plus_one_month
|
||||
|
||||
def translateMonth(month):
|
||||
MONTHS = [
|
||||
('Januar', 'JÄN', 'January'),
|
||||
('Februar', 'FEBR', 'February'),
|
||||
('März', 'MRZ', 'March'),
|
||||
('April', 'APR', 'April'),
|
||||
('Mai', 'MAI', 'May'),
|
||||
('Juni', 'JUN', 'June'),
|
||||
('Juli', 'JUL', 'July'),
|
||||
('August', 'AUG', 'August'),
|
||||
('September', 'SEP', 'September'),
|
||||
('Oktober', 'OKT', 'October'),
|
||||
('November', 'NOV', 'November'),
|
||||
('Dezember', 'DEZ', 'December'),
|
||||
]
|
||||
for mon in MONTHS:
|
||||
if month == mon[1]:
|
||||
return mon[2]
|
||||
|
||||
# Get Scraper name, item count and online_calendar (virtcal)
|
||||
def getScraper(venue, website, cal):
|
||||
virtcal = Calendar.objects.get(shortcode='000')
|
||||
ncal = Calendar.objects.get(shortcode=cal)
|
||||
try:
|
||||
scraper, created = Scraper.objects.get_or_create(
|
||||
name=venue.name,
|
||||
website=website,
|
||||
calendar = ncal,
|
||||
items = 0,
|
||||
new_items = 0,
|
||||
last_ran = datetime.now(),
|
||||
)
|
||||
venue.cal = ncal
|
||||
venue.save()
|
||||
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)
|
||||
return scraper, scraper.items, virtcal
|
||||
|
||||
# Update item_count of the Scraper at the end of the scrape
|
||||
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()
|
||||
print("Scaper Updated")
|
||||
return
|
||||
|
||||
# Get site HTML content for XPATH travel
|
||||
def getSource(browser, link):
|
||||
browser.get(link)
|
||||
sleep(6)
|
||||
ps = html.fromstring(browser.page_source)
|
||||
return ps
|
||||
|
||||
# Get Selenium Web Driver, with params for Chrome or Firefox
|
||||
# Or in production to run headless
|
||||
def getBrowser(run_env):
|
||||
if run_env == 'dev':
|
||||
print("Chrome is a go!")
|
||||
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()
|
||||
|
||||
# Create Dated URL with zero-padded numbers
|
||||
def createBasicURL(site_url):
|
||||
month = datetime.now().month
|
||||
next_month = odt_next_month.month
|
||||
year = datetime.now().year
|
||||
if next_month == 1:
|
||||
next_year = year+1
|
||||
links = [
|
||||
site_url + str(month) + "/" + str(year),
|
||||
site_url + str(next_month) + "/" + str(next_year)
|
||||
]
|
||||
else:
|
||||
links = [
|
||||
site_url + str(month) + "/" + str(year),
|
||||
site_url + str(next_month) + "/" + str(year)
|
||||
]
|
||||
return links
|
||||
|
||||
# Create Dated URL without zero-padded numbers
|
||||
def createURLNoZero(site_url):
|
||||
month = datetime.now().month
|
||||
next_month = odt_next_month.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
|
||||
|
||||
# Create Dated URL Link with zero-padding
|
||||
def createURL(site_url):
|
||||
month = datetime.now().month
|
||||
if month < 10:
|
||||
month = "0" + str(month)
|
||||
else:
|
||||
month = str(month)
|
||||
next_month = odt_next_month.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
|
||||
|
||||
# Create Dated URL with dashes
|
||||
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
|
||||
|
||||
# Add Calendar to Event Object (maybe extraneous)
|
||||
def add_calendar(event, calendar):
|
||||
# print("Add Calendar", type(event), event, calendar)
|
||||
if type(event) is tuple:
|
||||
event = event[0]
|
||||
cal = Calendar.objects.get(shortcode=calendar)
|
||||
event.calendar.add(cal)
|
||||
event.save()
|
||||
return event
|
||||
|
||||
# Add Calendars to Event Object ??
|
||||
def add_calendars(event, data):
|
||||
if type(data['calendars']) is not list:
|
||||
event.calendar.add(data['calendars'])
|
||||
else:
|
||||
for cal in data['calendars']:
|
||||
event.calendar.add(cal)
|
||||
event.save()
|
||||
return event
|
||||
|
||||
# Create Basic DigiSnaxx Event
|
||||
def createBasicEvent(event, event_type, venue):
|
||||
try:
|
||||
new_event, created = DSEvent.objects.update_or_create(
|
||||
event_type = event_type,
|
||||
show_title = event['title'],
|
||||
show_link = event['link'],
|
||||
show_date = event['dateStamp'],
|
||||
scraper = event['scraper'],
|
||||
venue = venue
|
||||
)
|
||||
new_event = add_calendars(new_event, event)
|
||||
print("\n+new event+")
|
||||
return new_event, created
|
||||
except Exception as e:
|
||||
print("DT Error: ", e)
|
||||
ppr(event)
|
||||
return None, None
|
||||
|
||||
# Create iCal Event
|
||||
def createBasiciCalEvent(event, event_type, venue):
|
||||
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%z %Z'),
|
||||
scraper = event['scraper'],
|
||||
venue = venue
|
||||
)
|
||||
new_event = add_calendars(new_event, event)
|
||||
print("Success")
|
||||
return new_event, created
|
||||
|
||||
def createDetailedEvent2(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"],
|
||||
more_details = event["details"],
|
||||
scraper = event['scraper'],
|
||||
venue = venue
|
||||
)
|
||||
new_event = add_calendars(new_event, event)
|
||||
print("Success")
|
||||
return new_event, created
|
||||
|
||||
# Create Detailed Event with Details & Guests
|
||||
# Details in JSON Format
|
||||
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"],
|
||||
guests = " ".join(event["guests"]),
|
||||
more_details = event["details"],
|
||||
scraper = event['scraper'],
|
||||
venue = venue
|
||||
)
|
||||
new_event = add_calendars(new_event, event)
|
||||
print("Success")
|
||||
return new_event, created
|
||||
|
||||
# Create iCal event from DF_Online & Medellin
|
||||
def createCleanIcalEvent(event, scraper, venue, event_type):
|
||||
new_date = event['eventDate']
|
||||
new_event = {}
|
||||
new_event['scraper'] = scraper
|
||||
new_event['calendars'] = scraper.calendar
|
||||
new_event['title'] = event['strSummary'],
|
||||
new_event['date'] = str(new_date),
|
||||
new_event['dateStamp'] = str(new_date),
|
||||
new_event['link'] = venue.website
|
||||
print("New Event")
|
||||
# ppr(new_event)
|
||||
createBasiciCalEvent(new_event, event_type, venue)
|
||||
|
||||
# Get events from iCal
|
||||
def getiCalEvents(gcal, scraper, venue, event_type):
|
||||
events = []
|
||||
for component in gcal.walk():
|
||||
event = {}
|
||||
event['scraper'] = scraper
|
||||
event['calendars'] = [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:
|
||||
print("what? ", e)
|
||||
|
||||
if event['strSummary'] != 'None':
|
||||
event['details'] = {
|
||||
"description" : event['strDesc'],
|
||||
"Location" : event['strLocation'],
|
||||
}
|
||||
events.append(event)
|
||||
return events
|
||||
|
||||
# Build iCal Events and Send to Create
|
||||
def buildiCalEvents(events, event_type, scraper, venue):
|
||||
for event in events:
|
||||
e = {}
|
||||
e['calendars'] = event['calendars']
|
||||
try:
|
||||
e['dateStamp'] = event['dateStart'][0]
|
||||
except:
|
||||
e['dateStamp'] = event['dateStart']
|
||||
e['title'] = event['strSummary']
|
||||
e['scraper'] = scraper
|
||||
e['link'] = venue.website
|
||||
try:
|
||||
createBasicEvent(e, event_type, venue)
|
||||
scraper.items+=1
|
||||
except Exception as e:
|
||||
print("Error: ", e)
|
||||
scraper.save()
|
||||
return
|
||||
|
||||
def getMDEVenue(venue, event):
|
||||
if venue.name == "DANCEFREE":
|
||||
venue.website = "https://www.instagram.com/dancefreeco"
|
||||
if venue.name == "Vintrash":
|
||||
venue.website = "https://www.instagram.com/vintrashbar"
|
||||
if venue.name == "The Wandering Paisa":
|
||||
venue.website = "https://wanderingpaisahostel.com"
|
||||
if venue.name == "Dulce Posion":
|
||||
venue.website = "https://www.instagram.com/dulceposionr"
|
||||
if venue.name == "Blood Dance Company":
|
||||
venue.website = "https://www.instagram.com/blooddancecompany"
|
||||
if venue.name == "OLSA Certified Spanish School":
|
||||
venue.website = "https://www.olsafoundation.org/"
|
||||
if event['strSummary'] == "Merli Rooftop Language Exchange":
|
||||
venue.website = "https://calendar.google.com/calendar/embed?src=46ae0446724b1b3ee83cbd7dbc0db6a235bf97509ad860ca91eada3c267b5e41%40group.calendar.google.com&ctz=America%2FBogota"
|
||||
if "Concious Warrior" in event['strSummary']:
|
||||
venue.website = "https://www.consciouscolombia.com/"
|
||||
venue.save()
|
||||
print(venue)
|
||||
return
|
||||
|
||||
# Get iCal events for Medellin & OnlineEvents
|
||||
def getiCalRepeateEvents(gcal, scraper, venue, event_type, cal):
|
||||
for component in gcal.walk():
|
||||
event = {}
|
||||
event['scraper'] = scraper
|
||||
event['calendars'] = [scraper.calendar]
|
||||
event['strSummary'] = f"{(component.get('SUMMARY'))}"
|
||||
event['strDesc'] = component.get('DESCRIPTION')
|
||||
event['strLocation'] = str(component.get('LOCATION'))
|
||||
event['dateStart'] = component.get('DTSTART')
|
||||
event['dateStamp'] = component.get('DTSTAMP')
|
||||
if event['strSummary'] != 'None':
|
||||
event['details'] = {
|
||||
"description" : event['strDesc'],
|
||||
"Location" : event['strLocation'],
|
||||
}
|
||||
if event['dateStamp'] != None:
|
||||
event['dateStart'] = event['dateStart'].dt
|
||||
event['dateStart'] = datetime.strptime(str(event['dateStart']) + " UTC", '%Y-%m-%d %H:%M:%S%z %Z')
|
||||
event['timezone'] = str(event['dateStart'])[-6:].strip()
|
||||
rules = component.get('RRule')
|
||||
try:
|
||||
if rules['FREQ'][0] == 'WEEKLY':
|
||||
date = datetime.today().date() - timedelta(days=datetime.today().weekday())
|
||||
date = datetime.combine(date, event['dateStart'].time())
|
||||
days = ["SU", "MO", "TU", "WE", "TH", "FR", "SA"]
|
||||
event = splitLocation(event, city="Medellin")
|
||||
for day in rules['BYDAY']:
|
||||
nday = days.index(day)
|
||||
if cal == 'mde':
|
||||
getMDEVenue(event['venue'],event)
|
||||
print(event)
|
||||
iCalEventRepeatFilter(nday, date, event, scraper, event['venue'], "Ed")
|
||||
|
||||
except Exception as e:
|
||||
print("Error: ", e, "\n\n")
|
||||
# ppr(event)
|
||||
pass
|
||||
|
||||
def iCalEventRepeatFilter(day, date, event, scraper, venue, event_type):
|
||||
print("repeate events")
|
||||
days = [day-1, day+6, day+13]
|
||||
for day in days:
|
||||
event['dateStamp'] = date + timedelta(days=day)
|
||||
dateStart = str(event['dateStamp']) + event['timezone'] + ' UTC'
|
||||
event['eventDate'] = dateStart
|
||||
createCleanIcalEvent(event, scraper, venue, event_type)
|
||||
return
|
||||
|
||||
def splitLocation(event, **kwargs):
|
||||
loc_split = event['strLocation'].split(',')
|
||||
# ppr(loc_split)
|
||||
venue_name = loc_split[0]
|
||||
venue, created = Organization.objects.get_or_create(
|
||||
name=venue_name,
|
||||
)
|
||||
event['venue'] = venue
|
||||
if kwargs['city']:
|
||||
venue.city = kwargs['city']
|
||||
venue.save()
|
||||
return event
|
||||
|
||||
# ARCHIVED Methods
|
||||
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
|
||||
95
contracts/migrations/0001_initial.py
Normal file
95
contracts/migrations/0001_initial.py
Normal file
@@ -0,0 +1,95 @@
|
||||
# Generated by Django 6.0.1 on 2026-02-18 19:41
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
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.CreateModel(
|
||||
name='Company',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=63)),
|
||||
('unq_entity_id', models.CharField(blank=True, max_length=63, null=True)),
|
||||
('website', models.URLField(blank=True, max_length=127, null=True)),
|
||||
('short_desc', models.CharField(blank=True, max_length=63, null=True)),
|
||||
('long_desc', models.TextField(blank=True, null=True)),
|
||||
('gmap_link', models.CharField(blank=True, max_length=253, null=True)),
|
||||
('address_complete', models.CharField(blank=True, max_length=127, null=True)),
|
||||
('address_numbers', models.CharField(blank=True, max_length=63, null=True)),
|
||||
('address_type', models.CharField(blank=True, max_length=31, null=True)),
|
||||
('city', models.CharField(blank=True, max_length=127, null=True)),
|
||||
('state', models.CharField(blank=True, max_length=127, null=True)),
|
||||
('zip_code', models.CharField(blank=True, max_length=15, null=True)),
|
||||
('tags', models.ManyToManyField(blank=True, to='contracts.tags')),
|
||||
],
|
||||
options={
|
||||
'verbose_name_plural': 'Companies',
|
||||
'ordering': ['name'],
|
||||
'unique_together': {('name', 'website')},
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Contract',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('title', models.CharField(max_length=31, unique=True)),
|
||||
('notice_id', models.CharField(blank=True, max_length=31, null=True)),
|
||||
('related_notice_id', models.CharField(blank=True, max_length=31, null=True)),
|
||||
('opp_type', models.CharField(blank=True, max_length=63, null=True)),
|
||||
('pub_date', models.DateField(blank=True, null=True)),
|
||||
('us_dept', models.CharField(blank=True, max_length=31, null=True)),
|
||||
('us_dept_sub_tier', models.CharField(blank=True, max_length=31, null=True)),
|
||||
('major_dept', models.CharField(blank=True, max_length=31, null=True)),
|
||||
('us_office', models.CharField(blank=True, max_length=31, null=True)),
|
||||
('award_date', models.CharField(blank=True, max_length=31, null=True)),
|
||||
('award_num', models.CharField(blank=True, max_length=31, null=True)),
|
||||
('unq_entity_id', models.CharField(blank=True, max_length=31, null=True)),
|
||||
('awarded_name', models.CharField(blank=True, max_length=31, null=True)),
|
||||
('awarded_addr', models.CharField(blank=True, max_length=31, null=True)),
|
||||
('contract_value', models.CharField(blank=True, max_length=31, null=True)),
|
||||
('orig_set_aside', models.CharField(blank=True, max_length=127, null=True)),
|
||||
('prod_svc_code', models.CharField(blank=True, max_length=127, null=True)),
|
||||
('naics_code', models.CharField(blank=True, max_length=127, null=True)),
|
||||
('contract_url', models.CharField(blank=True, max_length=127, null=True)),
|
||||
('description', models.TextField(blank=True, null=True)),
|
||||
('company', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contracts.company')),
|
||||
],
|
||||
options={
|
||||
'verbose_name_plural': 'Contracts',
|
||||
'ordering': ['pub_date', 'notice_id'],
|
||||
'unique_together': {('notice_id', 'unq_entity_id')},
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Exec',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=63)),
|
||||
('linkedin', models.URLField(blank=True, max_length=127, null=True)),
|
||||
('short_desc', models.CharField(blank=True, max_length=63, null=True)),
|
||||
('company', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contracts.company')),
|
||||
('tags', models.ManyToManyField(blank=True, to='contracts.tags')),
|
||||
],
|
||||
options={
|
||||
'verbose_name_plural': 'Execs',
|
||||
'ordering': ['name'],
|
||||
'unique_together': {('name', 'company')},
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,23 @@
|
||||
# Generated by Django 6.0.1 on 2026-02-18 19:56
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('contracts', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='contract',
|
||||
name='pub_date_txt',
|
||||
field=models.CharField(blank=True, max_length=63, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='contract',
|
||||
name='pub_date',
|
||||
field=models.DateTimeField(blank=True, null=True),
|
||||
),
|
||||
]
|
||||
19
contracts/migrations/0003_alter_contract_company.py
Normal file
19
contracts/migrations/0003_alter_contract_company.py
Normal file
@@ -0,0 +1,19 @@
|
||||
# Generated by Django 6.0.1 on 2026-02-18 20:08
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('contracts', '0002_contract_pub_date_txt_alter_contract_pub_date'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='contract',
|
||||
name='company',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='contracts.company'),
|
||||
),
|
||||
]
|
||||
19
contracts/migrations/0004_alter_contract_notice_id.py
Normal file
19
contracts/migrations/0004_alter_contract_notice_id.py
Normal file
@@ -0,0 +1,19 @@
|
||||
# Generated by Django 6.0.1 on 2026-02-18 20:09
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('contracts', '0003_alter_contract_company'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='contract',
|
||||
name='notice_id',
|
||||
field=models.CharField(default='000000000000000', max_length=31),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
||||
18
contracts/migrations/0005_alter_contract_title.py
Normal file
18
contracts/migrations/0005_alter_contract_title.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 6.0.1 on 2026-02-18 20:12
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('contracts', '0004_alter_contract_notice_id'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='contract',
|
||||
name='title',
|
||||
field=models.CharField(max_length=31),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,30 @@
|
||||
# Generated by Django 6.0.1 on 2026-02-18 23:04
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('contracts', '0005_alter_contract_title'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='contract',
|
||||
options={'ordering': ['-pub_date', 'notice_id'], 'verbose_name_plural': 'Contracts'},
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='contract',
|
||||
name='awarded_addr',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='contract',
|
||||
name='awarded_name',
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='company',
|
||||
name='unq_entity_id',
|
||||
field=models.CharField(blank=True, max_length=63, null=True, unique=True),
|
||||
),
|
||||
]
|
||||
18
contracts/migrations/0007_alter_company_unq_entity_id.py
Normal file
18
contracts/migrations/0007_alter_company_unq_entity_id.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 6.0.1 on 2026-02-18 23:05
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('contracts', '0006_alter_contract_options_remove_contract_awarded_addr_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='company',
|
||||
name='unq_entity_id',
|
||||
field=models.CharField(max_length=63, unique=True),
|
||||
),
|
||||
]
|
||||
18
contracts/migrations/0008_alter_contract_award_date.py
Normal file
18
contracts/migrations/0008_alter_contract_award_date.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 6.0.1 on 2026-02-18 23:21
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('contracts', '0007_alter_company_unq_entity_id'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='contract',
|
||||
name='award_date',
|
||||
field=models.DateField(blank=True, max_length=31, null=True),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,22 @@
|
||||
# Generated by Django 6.0.1 on 2026-02-18 23:32
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('contracts', '0008_alter_contract_award_date'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterUniqueTogether(
|
||||
name='contract',
|
||||
unique_together={('notice_id', 'unq_entity_id', 'pub_date_txt')},
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='contract',
|
||||
name='title',
|
||||
field=models.CharField(max_length=254),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,45 @@
|
||||
# Generated by Django 6.0.1 on 2026-02-19 02:20
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('contracts', '0009_alter_contract_unique_together_alter_contract_title'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='OriginalContract',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('number', models.CharField(max_length=31, unique=True)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Paragraph',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('date', models.DateField(blank=True, null=True)),
|
||||
('link', models.CharField(max_length=255, unique=True)),
|
||||
('paragraph', models.TextField(blank=True, null=True)),
|
||||
],
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='contract',
|
||||
name='award_date',
|
||||
field=models.DateField(blank=True, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='contract',
|
||||
name='original_contract_number',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='contracts.originalcontract'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='originalcontract',
|
||||
name='para',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contracts.paragraph'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,28 @@
|
||||
# Generated by Django 6.0.1 on 2026-02-19 02:33
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('contracts', '0010_originalcontract_paragraph_alter_contract_award_date_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='paragraph',
|
||||
name='date',
|
||||
field=models.DateField(),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='paragraph',
|
||||
name='link',
|
||||
field=models.CharField(max_length=255),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='paragraph',
|
||||
name='paragraph',
|
||||
field=models.TextField(),
|
||||
),
|
||||
]
|
||||
18
contracts/migrations/0012_alter_originalcontract_number.py
Normal file
18
contracts/migrations/0012_alter_originalcontract_number.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 6.0.1 on 2026-02-19 02:34
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('contracts', '0011_alter_paragraph_date_alter_paragraph_link_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='originalcontract',
|
||||
name='number',
|
||||
field=models.CharField(max_length=63),
|
||||
),
|
||||
]
|
||||
17
contracts/migrations/0013_alter_contract_unique_together.py
Normal file
17
contracts/migrations/0013_alter_contract_unique_together.py
Normal file
@@ -0,0 +1,17 @@
|
||||
# Generated by Django 6.0.1 on 2026-02-19 02:51
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('contracts', '0012_alter_originalcontract_number'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterUniqueTogether(
|
||||
name='contract',
|
||||
unique_together=set(),
|
||||
),
|
||||
]
|
||||
17
contracts/migrations/0014_alter_paragraph_options.py
Normal file
17
contracts/migrations/0014_alter_paragraph_options.py
Normal file
@@ -0,0 +1,17 @@
|
||||
# Generated by Django 6.0.1 on 2026-02-19 08:50
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('contracts', '0013_alter_contract_unique_together'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='paragraph',
|
||||
options={'ordering': ['-date'], 'verbose_name_plural': 'Paragraphs'},
|
||||
),
|
||||
]
|
||||
0
contracts/migrations/__init__.py
Normal file
0
contracts/migrations/__init__.py
Normal file
139
contracts/models.py
Normal file
139
contracts/models.py
Normal file
@@ -0,0 +1,139 @@
|
||||
from django.db import models
|
||||
from django.core.files.storage import FileSystemStorage
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
|
||||
class Paragraph(models.Model):
|
||||
date = models.DateField(blank=False, null=False, unique=False)
|
||||
link = models.CharField(max_length=255, unique=False)
|
||||
paragraph = models.TextField(blank=False, null=False)
|
||||
|
||||
|
||||
class Meta:
|
||||
verbose_name_plural = "Paragraphs"
|
||||
ordering = ['-date']
|
||||
|
||||
def __unicode__(self):
|
||||
return "%s" % self.date
|
||||
|
||||
def __str__(self):
|
||||
return u'%s' % self.date
|
||||
|
||||
|
||||
class OriginalContract(models.Model):
|
||||
number = models.CharField(max_length=63, unique=False)
|
||||
para = models.ForeignKey(Paragraph, on_delete=models.CASCADE)
|
||||
|
||||
def __unicode__(self):
|
||||
return "%s" % self.number
|
||||
|
||||
def __str__(self):
|
||||
return u'%s' % self.number
|
||||
|
||||
|
||||
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 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 Company(models.Model):
|
||||
name = models.CharField(max_length=63)
|
||||
unq_entity_id = models.CharField(max_length=63, blank=False, null=False, unique=True)
|
||||
website = models.URLField(max_length=127, blank=True, null=True)
|
||||
short_desc = models.CharField(max_length=63, blank=True, null=True)
|
||||
long_desc = models.TextField(blank=True, null=True)
|
||||
gmap_link = models.CharField(max_length=253, blank=True, null=True)
|
||||
tags = models.ManyToManyField(Tags, blank=True)
|
||||
|
||||
address_complete = models.CharField(max_length=127, blank=True, null=True)
|
||||
address_numbers = models.CharField(max_length=63, blank=True, null=True)
|
||||
address_type = models.CharField(max_length=31, blank=True, null=True)
|
||||
city = models.CharField(max_length=127, blank=True, null=True)
|
||||
state = models.CharField(max_length=127, blank=True, null=True)
|
||||
zip_code = models.CharField(max_length=15, blank=True, null=True)
|
||||
|
||||
|
||||
class Meta:
|
||||
unique_together = ("name", "website")
|
||||
verbose_name_plural = "Companies"
|
||||
ordering = ['name']
|
||||
|
||||
def __unicode__(self):
|
||||
return "%s" % self.name
|
||||
|
||||
def __str__(self):
|
||||
return u'%s' % self.name
|
||||
|
||||
|
||||
class Exec(models.Model):
|
||||
name = models.CharField(max_length=63)
|
||||
company = models.ForeignKey(Company, on_delete=models.CASCADE)
|
||||
linkedin = models.URLField(max_length=127, blank=True, null=True)
|
||||
short_desc = models.CharField(max_length=63, blank=True, null=True)
|
||||
tags = models.ManyToManyField(Tags, blank=True)
|
||||
|
||||
|
||||
class Meta:
|
||||
unique_together = ("name", "company")
|
||||
verbose_name_plural = "Execs"
|
||||
ordering = ['name']
|
||||
|
||||
def __unicode__(self):
|
||||
return "%s" % self.name
|
||||
|
||||
def __str__(self):
|
||||
return u'%s' % self.name
|
||||
|
||||
|
||||
class Contract(models.Model):
|
||||
title = models.CharField(max_length=254, unique=False)
|
||||
original_contract_number = models.ForeignKey(OriginalContract, on_delete=models.CASCADE, blank=True, null=True)
|
||||
company = models.ForeignKey(Company, blank=True, null=True, on_delete=models.CASCADE)
|
||||
notice_id = models.CharField(max_length=31, blank=False, null=False)
|
||||
related_notice_id = models.CharField(max_length=31, blank=True, null=True)
|
||||
opp_type = models.CharField(max_length=63, blank=True, null=True)
|
||||
pub_date = models.DateTimeField(blank=True, null=True)
|
||||
pub_date_txt = models.CharField(max_length=63, blank=True, null=True)
|
||||
us_dept = models.CharField(max_length=31, blank=True, null=True)
|
||||
us_dept_sub_tier = models.CharField(max_length=31, blank=True, null=True)
|
||||
major_dept = models.CharField(max_length=31, blank=True, null=True)
|
||||
us_office = models.CharField(max_length=31, blank=True, null=True)
|
||||
award_date = models.DateField(blank=True, null=True)
|
||||
award_num = models.CharField(max_length=31, blank=True, null=True)
|
||||
unq_entity_id = models.CharField(max_length=31, blank=True, null=True)
|
||||
contract_value = models.CharField(max_length=31, blank=True, null=True)
|
||||
orig_set_aside = models.CharField(max_length=127, blank=True, null=True)
|
||||
prod_svc_code = models.CharField(max_length=127, blank=True, null=True)
|
||||
naics_code = models.CharField(max_length=127, blank=True, null=True)
|
||||
contract_url = models.CharField(max_length=127, blank=True, null=True)
|
||||
description = models.TextField(blank=True, null=True)
|
||||
|
||||
|
||||
class Meta:
|
||||
# unique_together = ("notice_id", "unq_entity_id", "pub_date_txt")
|
||||
verbose_name_plural = "Contracts"
|
||||
ordering = ['-pub_date', 'notice_id']
|
||||
|
||||
def __unicode__(self):
|
||||
return "%s" % self.notice_id
|
||||
|
||||
def __str__(self):
|
||||
return u'%s' % self.notice_id
|
||||
27
contracts/serializers.py
Normal file
27
contracts/serializers.py
Normal file
@@ -0,0 +1,27 @@
|
||||
from rest_framework import serializers
|
||||
from .models import *
|
||||
|
||||
from django.db import models
|
||||
from django.contrib.auth.models import User
|
||||
from rest_framework.permissions import BasePermission
|
||||
|
||||
############
|
||||
## Events ##
|
||||
############
|
||||
|
||||
|
||||
class CompanySerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Company
|
||||
# fields = ('id', 'name', 'website', 'city', 'latitude', 'longitude', 'has_map')
|
||||
fields = '__all__'
|
||||
|
||||
|
||||
class ContractSerializer(serializers.ModelSerializer):
|
||||
company = CompanySerializer(many=False)
|
||||
# target_language = serializers.SerializerMethodField()
|
||||
class Meta:
|
||||
model = Contract
|
||||
fields = '__all__'
|
||||
depth = 2
|
||||
# fields = ('id', 'name',)
|
||||
3
contracts/tests.py
Normal file
3
contracts/tests.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
26
contracts/urls.py
Normal file
26
contracts/urls.py
Normal file
@@ -0,0 +1,26 @@
|
||||
"""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'^contracts/', ContractAPIView.as_view(), name="get-contracts-limit"),
|
||||
re_path(r'^contracts-all/', ContractAllAPIView.as_view(), name="get-contracts"),
|
||||
re_path(r'^companies/', CompanyAPIView.as_view(), name="get-companies"),
|
||||
# re_path(r'^events-token/', EventsTokenAPIView.as_view(), name="get-token-events"),
|
||||
|
||||
]
|
||||
64
contracts/views.py
Normal file
64
contracts/views.py
Normal file
@@ -0,0 +1,64 @@
|
||||
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 django.db.models import Count
|
||||
|
||||
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
|
||||
from rest_framework_api_key.permissions import HasAPIKey
|
||||
|
||||
td = timedelta(hours=7)
|
||||
odt = datetime.now() - td
|
||||
|
||||
# Create your views here.
|
||||
|
||||
class ContractAPIView(generics.ListAPIView):
|
||||
serializer_class = ContractSerializer
|
||||
queryset = Contract.objects.all()[:25]
|
||||
filter_backends = [DjangoFilterBackend, filters.SearchFilter]
|
||||
filterset_fields = ['id',]
|
||||
permission_classes = [HasAPIKey]
|
||||
|
||||
|
||||
class ContractAllAPIView(generics.ListAPIView):
|
||||
serializer_class = ContractSerializer
|
||||
queryset = Contract.objects.all()
|
||||
filter_backends = [DjangoFilterBackend, filters.SearchFilter]
|
||||
search_fields = ['notice_id', 'original_contract_number', 'title', 'description', 'company__name', 'us_dept', 'us_dept_sub_tier', 'us_office', 'naics_code', 'prod_svc_code']
|
||||
filterset_fields = ['id',]
|
||||
permission_classes = [HasAPIKey]
|
||||
|
||||
|
||||
class CompanyAPIView(generics.ListAPIView):
|
||||
serializer_class = CompanySerializer
|
||||
queryset = Company.objects.all()
|
||||
permission_classes = [HasAPIKey]
|
||||
|
||||
# class PromoAPIView(generics.ListAPIView):
|
||||
# serializer_class = PromoSerializer
|
||||
# queryset = Promo.objects.filter(published=True)
|
||||
# filterset_fields = ['organization__name', 'calendar__shortcode',]
|
||||
# search_fields = ['organization__name', 'calendar__shortcode',]
|
||||
# # permission_classes = [HasAPIKey]
|
||||
|
||||
# def get_queryset(self):
|
||||
# calendar = self.request.GET.get('calendar__shortcode')
|
||||
# queryset = Promo.objects.filter(published=True, calendar__shortcode=calendar).order_by('?')
|
||||
# return queryset
|
||||
|
||||
Reference in New Issue
Block a user