Skip to content
This repository has been archived by the owner on Aug 18, 2021. It is now read-only.

Commit

Permalink
Merge pull request #780 from OpenTechFund/#772-setup-react
Browse files Browse the repository at this point in the history
#772 setup react
  • Loading branch information
todd-dembrey authored Jan 7, 2019
2 parents 543c87d + c9f3f3f commit ea907d4
Show file tree
Hide file tree
Showing 23 changed files with 4,708 additions and 464 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,6 @@ wheels/

# Cache
.mypy_cache/

# Webpack
webpack-stats.json
15 changes: 14 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,21 @@ gulp watch

That will watch all fles for changes and build them with maps etc., perfect for development. (It will also run the "collecstatic" command, useful when running the site with a production server and not the built in dev server.)

If you are working on the React components then it may be worth just using one of the two following commands. They should do the same thing, but the npm command calls Webpack direct.

| WARNING: You cannot use Webpack watch and Hot Module Reload within vagrant, it must be run from your own installation of node. All other commands will work. |
| --- |

``` bash
gulp watch:app
# OR
npm run webpack-watch
```

To build the assets which get deployed, use the following. The deployment scripts will handle this, and the files do not need to be committed.

``` bash
gulp build
```

This will build all the files for production. For more command see the `gulpfile.js` file.
For more command see the `gulpfile.js` file.
2 changes: 1 addition & 1 deletion Vagrantfile
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ Vagrant.configure(2) do |config|
# Create a forwarded port mapping which allows access to a specific port
# within the machine from a port on the host machine. In the example below,
# accessing "localhost:8000" will access port 8000 on the guest machine.
config.vm.network "forwarded_port", guest: 8000, host: 8000
config.vm.network "forwarded_port", guest: 8000, host: 8000, id: "webserver"

# Create a private network, which allows host-only access to the machine
# using a specific IP.
Expand Down
59 changes: 57 additions & 2 deletions gulpfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,13 @@ options.theme = {
root : options.rootPath.theme,
sass : options.rootPath.theme + 'src/sass/',
js : options.rootPath.theme + 'src/javascript/',
app : options.rootPath.theme + 'src/app/',
img : options.rootPath.theme + 'src/images/',
font : options.rootPath.theme + 'src/fonts/',
dest : options.rootPath.app + 'static_compiled/',
css : options.rootPath.app + 'static_compiled/css/',
js_dest : options.rootPath.app + 'static_compiled/js/',
app_dest : options.rootPath.app + 'static_compiled/app/',
img_dest : options.rootPath.app + 'static_compiled/images/',
font_dest : options.rootPath.app + 'static_compiled/fonts/'
};
Expand Down Expand Up @@ -60,8 +62,16 @@ var gulp = require('gulp'),
sass = require('gulp-sass'),
cleanCSS = require('gulp-clean-css'),
touch = require('gulp-touch-cmd'),
webpack = require('webpack'),
webpackStrm = require('webpack-stream'),
DevServer = require('webpack-dev-server'),
exec = require('child_process').exec;


// Load webpack config
var webpackDev = () => require(options.theme.app + 'webpack.dev.config.js');
var webpackProd = () => require(options.theme.app + 'webpack.prod.config.js');

// The sass files to process.
var sassFiles = [
options.theme.sass + '**/*.scss',
Expand Down Expand Up @@ -160,6 +170,20 @@ gulp.task('scripts:production', gulp.series('clean:js', function js () {
.pipe(gulp.dest(options.theme.js_dest));
}));

// Build App.
gulp.task('app', function() {
return gulp.src(options.theme.app + 'src/')
.pipe(webpackStrm( webpackDev() ))
.pipe(gulp.dest(options.theme.app_dest));
})

// Build Prod App
gulp.task('app:production', function() {
return gulp.src(options.theme.app + 'src/')
.pipe(webpackStrm( webpackProd() ))
.pipe(gulp.dest(options.theme.app_dest));
})

// Copy images.
gulp.task('images', function copy () {
return gulp.src(options.theme.img + '**/*.*').pipe(gulp.dest(options.theme.img_dest));
Expand Down Expand Up @@ -208,13 +232,44 @@ gulp.task('watch:static', function watch () {
return gulp.watch(options.theme.dest + '**/*.*', options.gulpWatchOptions, gulp.series('collectstatic'));
});

gulp.task('watch:app', function watch (callback) {
var webpackOptions = webpackDev();

webpackOptions.entry.unshift(
`webpack-dev-server/client?http://localhost:${webpackOptions.devServer.port}/`,
`webpack/hot/dev-server`
);

var serverOptions = Object.assign(
{}, webpackOptions.devServer, {
publicPath: '/app/',
stats: {
colors: true,
cached: false,
cachedAssets: false
}
}
);

var server = new DevServer(
webpack( webpackOptions ),
serverOptions
)

server.listen(3000, "localhost", function(err) {
if(err) throw new console.PluginError("webpack-dev-server", err);
// Server listening
console.log("[webpack-dev-server]", "Running");
});
})

gulp.task('watch', gulp.parallel('watch:css', 'watch:lint:sass', 'watch:js', 'watch:lint:js', 'watch:images', 'watch:fonts', 'watch:static'));

// Build everything.
gulp.task('build', gulp.series(gulp.parallel('styles:production', 'scripts:production', 'images', 'fonts', 'lint'), 'collectstatic'));
gulp.task('build', gulp.series(gulp.parallel('styles:production', 'scripts:production', 'app:production', 'images', 'fonts', 'lint'), 'collectstatic'));

// Deploy everything.
gulp.task('deploy', gulp.parallel('styles:production', 'scripts:production', 'images', 'fonts'));
gulp.task('deploy', gulp.parallel('styles:production', 'scripts:production', 'app:production', 'images', 'fonts'));

// The default task.
gulp.task('default', gulp.series('build'));
3 changes: 2 additions & 1 deletion opentech/apply/activity/tests/test_messaging.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ def log_message(self, message, recipient, event, status):
pass


class AdapterMixin:
@override_settings(ROOT_URLCONF='opentech.apply.urls')
class AdapterMixin(TestCase):
adapter = None

def process_kwargs(self, message_type, **kwargs):
Expand Down
4 changes: 4 additions & 0 deletions opentech/apply/funds/models/applications.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,10 @@ def get_landing_page_template(self, request, *args, **kwargs):
# Make sure all children use the shared template
return 'funds/round_landing.html'

@cached_property
def fund(self):
return self.get_parent()

@property
def is_sealed(self):
return self.sealed and self.is_open
Expand Down
25 changes: 25 additions & 0 deletions opentech/apply/funds/templates/funds/submissions_by_round.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{% extends "base-apply.html" %}
{% load render_bundle from webpack_loader %}

{% block title %}{{ object }}{% endblock %}

{% block content %}
<div class="admin-bar">
<div class="admin-bar__inner">
<div>
<h5><a href="{% url "apply:submissions:list" %}">< Submissions</a></h5>
<h1 class="gamma heading heading--no-margin heading--bold">{{ object }}</h1>
<h5>{% if object.fund %}{{ object.fund }} | {% endif %}Lead: {{ object.lead }}</h5>
</div>
</div>
</div>

<div class="wrapper wrapper--large">
<div id="react-app">
<h2>THERE WILL BE A TABLE HERE</h2>
</div>
</div>

{% render_bundle 'main' %}

{% endblock %}
12 changes: 7 additions & 5 deletions opentech/apply/funds/tests/factories/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,7 @@ def parent(self, create, extracted_parent, **parent_kwargs):
if extracted_parent and parent_kwargs:
raise ValueError('Cant pass a parent instance and attributes')

if not extracted_parent:
parent = ApplyHomePageFactory(**parent_kwargs)
else:
# Assume root node if no parent passed
parent = extracted_parent
parent = extracted_parent or ApplyHomePageFactory(**parent_kwargs)

parent.add_child(instance=self)

Expand Down Expand Up @@ -144,6 +140,12 @@ class Params:
end_date = factory.Sequence(lambda n: datetime.date.today() + datetime.timedelta(days=7 * (n + 1)))
lead = factory.SubFactory(StaffFactory)

@factory.post_generation
def parent(self, create, extracted_parent, **parent_kwargs):
if create:
parent = extracted_parent or FundTypeFactory(**parent_kwargs)
parent.add_child(instance=self)

@factory.post_generation
def forms(self, create, extracted, **kwargs):
if create:
Expand Down
50 changes: 50 additions & 0 deletions opentech/apply/funds/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
ApplicationSubmissionFactory,
ApplicationRevisionFactory,
InvitedToProposalFactory,
LabFactory,
LabSubmissionFactory,
RoundFactory,
SealedRoundFactory,
SealedSubmissionFactory,
)
Expand Down Expand Up @@ -547,3 +549,51 @@ def test_can_view_multiple_sealed(self):
self.assertTrue('peeked' in self.client.session)
self.assertTrue(str(first.id) in self.client.session['peeked'])
self.assertTrue(str(second.id) in self.client.session['peeked'])


class ByRoundTestCase(BaseViewTestCase):
url_name = 'apply:submissions:{}'
base_view_name = 'by_round'

def get_kwargs(self, instance):
return {'pk': instance.id}


class TestStaffSubmissionByRound(ByRoundTestCase):
user_factory = StaffFactory

def test_can_access_round_page(self):
new_round = RoundFactory()
response = self.get_page(new_round)
self.assertContains(response, new_round.title)

def test_can_access_lab_page(self):
new_lab = LabFactory()
response = self.get_page(new_lab)
self.assertContains(response, new_lab.title)

def test_cant_access_normal_page(self):
new_round = RoundFactory()
page = new_round.get_site().root_page
response = self.get_page(page)
self.assertEqual(response.status_code, 404)


class TestApplicantSubmissionByRound(ByRoundTestCase):
user_factory = UserFactory

def test_cant_access_round_page(self):
new_round = RoundFactory()
response = self.get_page(new_round)
self.assertEqual(response.status_code, 403)

def test_cant_access_lab_page(self):
new_lab = LabFactory()
response = self.get_page(new_lab)
self.assertEqual(response.status_code, 403)

def test_cant_access_normal_page(self):
new_round = RoundFactory()
page = new_round.get_site().root_page
response = self.get_page(page)
self.assertEqual(response.status_code, 403)
2 changes: 2 additions & 0 deletions opentech/apply/funds/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from .views import (
RevisionCompareView,
RevisionListView,
SubmissionsByRound,
SubmissionDetailView,
SubmissionEditView,
SubmissionListView,
Expand Down Expand Up @@ -31,6 +32,7 @@
path('', include('opentech.apply.determinations.urls', namespace="determinations")),
path('revisions/', include(revision_urls, namespace="revisions")),
])),
path('rounds/<int:pk>/', SubmissionsByRound.as_view(), name="by_round"),
], 'submissions')


Expand Down
20 changes: 18 additions & 2 deletions opentech/apply/funds/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from django.contrib.auth.decorators import login_required
from django.contrib import messages
from django.core.exceptions import PermissionDenied
from django.http import HttpResponseRedirect
from django.http import HttpResponseRedirect, Http404
from django.shortcuts import get_object_or_404
from django.urls import reverse_lazy
from django.utils.decorators import method_decorator
Expand All @@ -14,6 +14,8 @@
from django_filters.views import FilterView
from django_tables2.views import SingleTableMixin

from wagtail.core.models import Page

from opentech.apply.activity.views import (
AllActivityContextMixin,
ActivityContextMixin,
Expand All @@ -28,7 +30,7 @@

from .differ import compare
from .forms import ProgressSubmissionForm, UpdateReviewersForm, UpdateSubmissionLeadForm
from .models import ApplicationSubmission, ApplicationRevision
from .models import ApplicationSubmission, ApplicationRevision, RoundBase, LabBase
from .tables import AdminSubmissionsTable, SubmissionFilter, SubmissionFilterAndSearch
from .workflow import STAGE_CHANGE_ACTIONS

Expand Down Expand Up @@ -418,3 +420,17 @@ def get_context_data(self, **kwargs):
to_revision = self.object.revisions.get(id=self.kwargs['to'])
self.compare_revisions(from_revision, to_revision)
return super().get_context_data(**kwargs)


@method_decorator(staff_required, name='dispatch')
class SubmissionsByRound(DetailView):
model = Page
template_name = 'funds/submissions_by_round.html'

def get_object(self):
# We want to only show lab or Rounds in this view, their base class is Page
obj = super().get_object()
obj = obj.specific
if not isinstance(obj, (LabBase, RoundBase)):
raise Http404(_("No Round or Lab found matching the query"))
return obj
8 changes: 8 additions & 0 deletions opentech/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@
'hijack',
'compat',
'pagedown',
'webpack_loader',

'django.contrib.admin',
'django.contrib.auth',
Expand Down Expand Up @@ -592,3 +593,10 @@

REFERRER_POLICY = env.get('SECURE_REFERRER_POLICY',
'no-referrer-when-downgrade').strip()

WEBPACK_LOADER = {
'DEFAULT': {
'BUNDLE_DIR_NAME': 'app/',
'STATS_FILE': os.path.join(BASE_DIR, './opentech/static_compiled/app/webpack-stats-prod.json'),
}
}
5 changes: 5 additions & 0 deletions opentech/settings/dev.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,8 @@
MIDDLEWARE = [
'debug_toolbar.middleware.DebugToolbarMiddleware',
] + MIDDLEWARE


WEBPACK_LOADER['DEFAULT'].update({
'STATS_FILE': os.path.join(BASE_DIR, './opentech/static_compiled/app/webpack-stats.json'),
})
1 change: 0 additions & 1 deletion opentech/settings/production.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,4 @@
"WEBHOOK_SECRET": env.get('ANYMAIL_WEBHOOK_SECRET', None)
}


django_heroku.settings(locals())
Loading

0 comments on commit ea907d4

Please sign in to comment.