Skip to content

Feat: add campaign creation automation#583

Open
ItsAbhinavM wants to merge 8 commits into
hatnote:masterfrom
ItsAbhinavM:582-campaign-creation-automation
Open

Feat: add campaign creation automation#583
ItsAbhinavM wants to merge 8 commits into
hatnote:masterfrom
ItsAbhinavM:582-campaign-creation-automation

Conversation

@ItsAbhinavM

@ItsAbhinavM ItsAbhinavM commented Jun 15, 2026

Copy link
Copy Markdown
Collaborator

Description

Fixes #582

This PR adds support for "Campaign Creation Request" on Montage. Creates new pages like /requests, /requests/new and requests/MNTG-6772.

Changes made:

Frontend

  • CampaignRequestform: A forms like page where the user adds the detail of campaign and submits it.
  • Campaignrequestdetail: Displays campaign details after submission
  • Campaignrequests: Page which displays all campaign requests details
  • adminService.js: Updated endpoints of campaign requests.

Backend

A new DAO CampaignRequestDAO has been made along with its helper functions on admin_endpoints.py. Each request will receive an ID like MNTG-xxxx ( MNTG - Monitoring ) so that it can be tracked. New endpoints also has been made supporting the feature.

/admin/campaign_requests
/admin/campaign_requests/validate_user
/admin/campaign_requests/<request_id>
/admin/campaign_requests/submit
/admin/campaign_requests/<request_id>/approve
/admin/campaign_requests/<request_id>/advise
/admin/campaign_requests/<request_id>/resubmit

UI Changes

image image image image image

status: In progress, UI is and some UX additions are yet to be added Completed

@ItsAbhinavM

Copy link
Copy Markdown
Collaborator Author

@lgelauff The PR is complete now, could you please review this and suggest changes if required

@ItsAbhinavM

Copy link
Copy Markdown
Collaborator Author

Please don't mind the changes in RoundEdit page as it came to satisfy the linting issues which appeared on the PR pipeline.

@ItsAbhinavM

Copy link
Copy Markdown
Collaborator Author

@lgelauff if you require the screen recording of this feature, please do tell. Since this is a relatively big PR screen recording of larger duration will not be supported in Github comment ( 10MB max ). I might have to upload the recording to my cloud which might take time.

@lgelauff

Copy link
Copy Markdown
Collaborator

@lgelauff if you require the screen recording of this feature, please do tell. Since this is a relatively big PR screen recording of larger duration will not be supported in Github comment ( 10MB max ). I might have to upload the recording to my cloud which might take time.

A recording would be super helpful. A few shorter ones would also help. Feel free to push the PR to montage-beta!

@lgelauff lgelauff left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if I fully understand the design. I should have asked earlier, but it would be helpful if you can explain in the issue your big picture design. Especially try to make explicit the assumptions you're making, who's filling what out, and what happens before/after the request.

It seems some of your functions resemble some functionality elsewhere in the code, and make me wonder if we can reuse some.

My coding assistant was suggesting some more functional problems, but maybe it's more helpful to deal with this first.

meta: { requiresAuth: true }
},
{
path: '/requests/:requestId',

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this different from request_id ?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this page is used for viewing the description of the request, its the campaignRequestDetail.vue page.

Comment thread montage/rdb.py
Lifecycle: pending -> approved (campaign_id is set)
-> needs_clarification -> pending (resubmit)
"""
__tablename__ = 'campaign_requests'

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please include migration in tools/migrate_prod_db.sql

Comment thread montage/rdb.py
qs = qs.order_by(Campaign.id)
return qs.first()

class CampaignRequestDAO(object):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this supposed to be the same as the earlier declared class?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I'm sorry this was a separate class which I had for debugging purpose, will remove them.

Comment thread frontend/src/views/CampaignRequests.vue Outdated
</cdx-button>
</router-link>
</div>
<!-- Add v-if="isSuperuser" clause for this div later -->

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

leftover todo?


// const props = defineProps({
// isSuperuser: { type: Boolean, default: false }
// })

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

debug code?

Comment thread montage/rdb.py Outdated
self.rdb_session.add(flag)

def _generate_request_id():
suffix = ''.join(random.choices(string.digits, k=4)) # MNTG - Monitoring

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please verify collision probability at expected rates.
Why do we need random?

</tr>
</thead>
<tbody>
<tr v-for="tz in COMMON_TIMEZONES" :key="tz.id">

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any thoughts on how we handle times elsewhere in Montage?

raise InvalidAction('username query parameter is required.')
return {'data': {'username': username, 'exists': _wikimedia_user_exists(username)}}

def _wikimedia_user_exists(username):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would we be able to reuse existing functionality?

users = resp.json().get('query', {}).get('users', [])
return bool(users) and 'missing' not in users[0]
except Exception:
return True

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This confuses me. Does that mean if there's an exception you'd accept anything?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I attempted for a fail-true approach, incase the API gets down, I think currently the exception is a bit too broad as it could handle any edge case. Will change it to make it specifically for RequestException

@ItsAbhinavM

Copy link
Copy Markdown
Collaborator Author

@lgelauff I've made the changes as per your review, here is the video snippet of the feature.

2026-06-28.10-39-16.1.mp4

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Montage support for campaign creation requests

2 participants